Preface: Internet User's Guide and Catalog, by Ed Krol. Unpacking Software Is Basically A Matter of
Preface: Internet User's Guide and Catalog, by Ed Krol. Unpacking Software Is Basically A Matter of
Preface
This book is about porting software between UNIX platforms, the process of taking a soft-
ware package in source form and installing it on your machine. This doesnt sound like a big
deal at first, but theres more to it than meets the eye: you need to know how to get the soft-
ware, how to unpack what you get, how to modify the package so that it will compile on your
system, how to compile and install the software on your system, and how to deal with prob-
lems if they crop up.
Nevertheless, it doesnt involve anything that hasnt already been done to death in hundreds of
well-written books: you can find out about getting software from the Internet in The Whole
Internet Users Guide and Catalog, by Ed Krol. Unpacking software is basically a matter of
using standard tools described in dozens of good introductory textbooks. Compiling pro-
grams is so simple that most C textbooks deal with it in passing. Installation is just a matter
of copying software to where you want it. Programming is the meat of lots of books on UNIX
programming, for example Advanced Programming in the UNIX environment by Richard
Stevens,
So why yet another book?
Most textbooks give you an idealized view of programming: This is the way to do it (and it
works). They pay little attention to the ways things can go wrong. UNIX is famed for cryp-
tic or misleading error messages, but not many books go into the details of why they appear or
what they really mean. Even experienced programmers frequently give up when trying to port
software. The probable advantage of completing the port just isnt worth effort that it takes.
In this book, Id like to reduce that effort.
If you take all the books I just mentioned, youll have to find about 3 feet of shelf space to
hold them. Theyre all good, but they contain stuff that you dont really want to know about
right now (in fact, youre probably not sure if you ever want to know all of it). Maybe you
have this pressing requirement to get this debugger package, or maybe you finally want to get
the latest version of nethack up and running, complete with X11 support, and the last thing
you want to do on the way is go through those three feet of paper.
Thats where this book comes in. It covers all issues of porting, from finding the software
through porting and testing up to the final installation, in the sequence in which you perform
them. It goes into a lot of detail comparing the features of many different UNIX systems, and
offers suggestions about how to emulate features not available on the platform to which you
are porting. It views the problems from a practical rather than from a theoretical perspective.
You probably wont know any more after reading it than you would after reading the in-depth
books, but I hope that youll find the approach more related to your immediate problems.
Audience
This book is intended for anybody who has to take other peoples software and compile it on a
UNIX platform. It should be of particular interest to you if youre:
A software developer porting software to a new platform.
A system administrator collecting software products for your system.
A computer hobbyist collecting software off the Internet.
Whatever your interest, I expect that youll know UNIX basics. If youre a real newcomer,
you might like to refer to Learning the UNIX Operating System, by Grace Todino, John
Strang and Jerry Peek. In addition, UNIX in a Nutshell, available in BSD and System V
flavours, includes a lot of reference material which I have not repeated in this book.
The less you already know, the more use this book is going to be to you, of course. Neverthe-
less, even if youre an experienced programmer, you should find a number of tricks to make
life easier.
Organization
One of the big problems in porting software is that you need to know everything first. While
writing this book I had quite a problem deciding the order in which to present the material. In
the end, I took a two-pronged approach, and divided this book into two major parts:
1. In the first part, well look at the stages through which a typical port passes: getting the
software, extracting the source archives, configuring the package, compiling the soft-
ware, testing the results, and installing the completed package.
2. In the second part, well take a look at the differences between different flavours of
UNIX, how they can make life hard for you, and how we can solve the problems.
* UNIX is, of course, a registered trademark of its current owner. In this context, I am referring to any
operating system that presents a UNIX-like interface to the user and the programmer.
I have tried to make the examples in this book as close to practice as possible, and most are
from real-life sources. A book is not a monitor, however, and displays that look acceptable
(well, recognizable) on a monitor can sometimes look really bad in print. In particular, the
utilities used in porting sometimes print out lines of several hundred characters. I have tried
to modify such output in the examples so that it fits on the page. For similar reasons, I have
modified the line breaks in some literally quoted texts, and have occasionally squeezed things
like long directory listings.
Terminology
Any technical book uses jargon and technical terms that are not generally known. Ive tried to
recognize the ones used in this book and describe them when they occur. Apart from this, I
will be particularly pedantic about the way I use the following terms in this book:
program Everybody knows what a program is: a series of instructions to the computer
which, when executed, cause a specific action to take place. Source files dont fit
this category: a source program (a term you wont find again in this book) is
really a program source (a file that you can, under the correct circumstances, use
to create a program). A program may, however, be interpreted, so a shell script
may qualify as a program. So may something like an emacs macro, whether byte
compiled or not (since emacs can interpret uncompiled macros directly).
Acknowledgements
Without software developers all over the world, there would be nothing to write about. In par-
ticular, the Free Software Foundation and the Computer Sciences Research Group in Berkeley
(now defunct) have given rise to an incredible quantity of freely available software. Special
thanks go to the reviewers Larry Campbell and Matt Welsh, and particularly to James Cox,
Jerry Dunham, and Jrg Micheel for their encouragement and meticulous criticism of what
initially was just trying to be a book. Thanks also to Clive King of the University of Aberyst-
wyth for notes on data types and alignment, Steve Hiebert with valuable information about
HP-UX, and Henry Spencer and Jeffrey Friedl for help with regular expressions.
Finally, I cant finish this without mentioning Mike Loukides and Andy Oram at OReilly and
Associates, who gently persuaded me to write a book about porting, rather than just present-
ing the reader with a brain dump.
What is porting?
Its difficult to make a clear distinction between porting and building. In this book, well use
three terms:
building a package is the planned process of creating an installable software package.
This is essentially the content of Chapter 5, Building the package.
installation is the planned process of putting an installable software package where users
can use it. This is what we talk about in Chapter 9, Installation.
Some people use the term porting to describe a software installation requiring undocu-
mented changes to adapt it to a new environment, not including the process of configura-
tion if this is intended to be part of the build process. Although this is a useful definition,
it contains an element of uncertainty: when you start, you dont know whether this is
going to be a build or a port. Its easier to call the whole process porting, whether you
just have to perform a simple build or complicated modifications to the source. Thats
the way well use the term in this book.
The effort required to port a package can vary considerably. If you are running a SparcStation
and get some software developed specifically for SparcStations, and the software does not
offer much in the way of configuration options, you probably really can get it to run by read-
ing the sources onto disk, and typing make and make install. This is the exception, how-
ever, not the rule. Even with a SparcStation, you might find that the package is written for a
different release of the operating system, and that this fact requires significant modifications.
A more typical port might include getting the software, configuring the package, building the
1
package, formatting and printing the documentation, testing the results and installing files in
the destination directories.
On an Intel 486/66, configure runs for 15 seconds, make runs for about 85 seconds, and make
install runs for about 5 secondsall in all, less than two minutes. If everything were that
simple, nobody would need this book.
On the other hand, this simple view omits a point or two. bison comes with typeset documen-
tation. Like most products of the Free Software Foundation, it is written in texinfo format,
which relies on TEX for formatting. It doesnt get formatted automatically. In fact, if you
look for the target in the Makefile, youll find that there isnt one: the Makefile ignores printed
documentation. I consider this a bug in the Makefile. Never mind, its easy enough to do it
manually:
$ tex bison.texinfo
tex: not found
This is a fairly typical occurrence in porting: in order to port a package, you first need to port
three other, more complicated packages. In fact, most ports of bison are made in order to
compile some other product, such as the GNU C compiler. In order to get our documentation
printed, we first need to port TEX, which is appropriately depicted in its own printed documen-
tation as a shaggy lion. This is definitely a non-trivial port: TEX consists of dozens of differ-
ent parts, the source tree varies greatly depending on where you get it from, the whole thing is
written in Web, Donald Knuths own private dialect of Pascal, and once you get it to run you
* bison is a parser generator, compatible with yacc.
discover that the output (deliberately) does not match any printer available, and that you need
a so-called printer driver to output it to your favourite laser printeryet another port.
Under these circumstances, it wouldnt be surprising if you give up and rely on the online
documentation supplied with bison. bison has two different online reference documents: a
man page and something called info, a cross-linked documentation reader from the Free Soft-
ware Foundation. The man page is two pages long, the info runs to over 200K in five files.
There are no prizes for guessing where the real information is. But how do you run info?
Simple: you port the GNU texinfo package. This time its not quite as bad as porting TEX, but
its still more difficult than porting bison.
This scenario is fairly typical: you set out to port something simple, and everything seems to
be fine, and then you find that a minor part of the port can really take up lots of time. Typi-
cally, this is the point where most people give up and make do with what they have achieved.
This book is intended to help you go the whole distance.
Unix flavours
UNIX spent the first ten years of its existence as the object of computer science research.
Developed in Bell Labs (part of AT&T), it was significantly extended in the University of Cal-
ifornia at Berkeley (UCB), which started releasing significant updates, the so-called Berkeley
Software Distribution (BSD) in 1977. By the time AT&T decided to commercialize UNIX
with System III in the early 80s, the fourth BSD was already available, and both System III
and System V drew heavily from it. Nevertheless, the differences were significant, and
despite the advent of System V.4, which basically just added all features available in any
UNIX dialect into one package, the differences remain. A good overview of the relationship
between the Unixes can be found on page 5 of The Design and the Implementation of the
4.3BSD UNIX Operating System by Sam Leffler, Kirk McKusick, Mike Karels and John
Quarterman. In this book I will concentrate on the differences that can be of importance when
porting from one flavour to another.
Research UNIX
Research UNIX is the original UNIX that has been developed inside Bell Labs since 1969.
The last version that became widely available was the Seventh Edition, in 1978. This version
can be considered the granddaddy of them all*, and is also frequently called Version 7. In this
book, Ill make frequent references to this version. Work on Research UNIX continued until
1993, by which time it had reached the Tenth Edition. Its unlikely that youll have much to
do with it directly, but occasionally ideas from Research UNIX trickle into other flavours.
XENIX
XENIX is a version of UNIX developed by Microsoft for Intel architectures in the early 80s.
It was based mainly on the System III versions available at the time, though some ideas from
other versions were included and a significant amount of work was put into making it an eas-
ier system to live with. Not much effort was put into making it compatible with other versions
of UNIX, however, and so you can run into a few surprises with XENIX. SCO still markets it,
* In fact, a number of UNIX flavours, including System V and BSD, can trace their origins back to the
Sixth Edition of 1976, but they all benefitted from modifications made in the Seventh Edition.
System V
System V was derived from the 6th and 7th editions via System III, with a certain amount bor-
rowed from 4.0BSD. It has become the standard commercial UNIX, and is currently the only
flavour allowed to bear the UNIX trademark. It has evolved significantly since its introduc-
tion in 1982, with borrowings from Research UNIX and BSD at several points along the way.
Currently available versions are V.3 (SCO Open Desktop) and V.4 (almost everybody else).
System V.3 lacked a number of features available in other Unixes, with the result that almost
all V.3 ports have borrowed significantly from other versions, mainly 4.2BSD. The result is
that you cant really be sure what you have with System V.3 you need to consult the docu-
mentation for more information. In particular, vanilla System V.3 supports only the original
UNIX file system, with file names length limited to 14 characters and with no symbolic links.
It also does not have a standard data communications interface, though both BSD sockets and
System V STREAMS have been ported to it.
System V.3.2 is, as its name suggests, a version of System V.3. This version includes compati-
bility with XENIX system calls. As we saw above, XENIX went its own way for some time,
resulting in incompatibilities with System V. These XENIX features should be supported by
the kernel from System V.3.2 onwards. SCO UNIX is version V.3.2, and includes STREAMS
support.
System V.4 is the current version of System V. Previous versions of System V were often criti-
cized for lacking features. This cannot be said of System V.4: it incorporates System V.3.2
(which already incorporates XENIX), 4.3BSD, and SunOS. The result is an enormous system
which has three different ways to do many things. It also still has significant bugs.
Developing software under System V.4 is an interesting experience. Since the semantics of
System V.3 and BSD differ in some areas, System V.4 supplies two separate sets of libraries,
one with a System V personality and one with a BSD personality. There are no prizes for
guessing which is more reliable: unless you really need to, you should use the System V
libraries. When we discuss kernel and library differences in Part 2 of the book, the statement
This feature is supported by System V.4 will mean that the System V library interface sup-
ports it. The statement This feature is supported by BSD also implies that it should be sup-
ported by the BSD library interface of System V.4.
OSF/1
OSF/1 is a comparatively recent development in the UNIX market. It was developed by the
Open Systems Foundation, an industry consortium formed as a result of dissatisfaction with
AT&Ts policy on UNIX. The kernel is based on CMUs Mach operating system, a so-called
microkernel*. The original Mach operating system was styled on Berkeley UNIX. OSF/1
attempts to offer the same functionality as System V, though inevitably some incompatibilities
* A microkernel operating system is an operating system that leaves significant operating system func-
tionality to external components, usually processes. For example, device drivers and file systems are fre-
quently implemented as separate processes. It does not imply that the complete system is any smaller or
less functional than the monolithic UNIX kernel.
exist.
POSIX.1
POSIX is a series of emerging IEEE standards applying to operating systems, utilities, and
programming languages. The relevant standard for operating systems is IEEE 1003.1-1990,
commonly called POSIX.1. It has also been adopted by the International Standards Organiza-
tion (ISO) as standard ISO/IEC 9945.1:1990.
POSIX.1 defines the interface between application programs and the operating system, and
makes no demands on the operating system except that it should supply the POSIX.1 inter-
face. POSIX.1 looks very much like a subset of UNIX. In fact, most users wouldnt notice
the difference. This makes it easy for UNIX operating systems to supply a POSIX.1 interface.
Other operating systems might need much more modification to become POSIX.1 compliant.
From a UNIX viewpoint, POSIX.1 does not supply as rich a set of functions as any of the
commercially available UNIX flavours, so programming to POSIX specifications can feel
somewhat restrictive. This matter is discussed in the POSIX Programmers Guide by Donald
Lewine.
Despite these slight disadvantages, POSIX has a great influence on operating system develop-
ment: all modern flavours of UNIX claim to be POSIX-compliant, although the degree of suc-
cess varies somewhat, and other systems are also attempting to supply a POSIX.1 interface.
The trend is clear: future UNIX-like operating systems will be POSIX-compliant, and if you
stick to POSIX features, your porting problems will be over. And I have a supply of bridges
for sale, first come, first served.
Other flavours
It doesnt take much effort to add a new feature to a kernel, and people do it all the time. The
result is a proliferation of systems that mix various features of the leading products and addi-
tional features of their own. On top of that, the release of kernel sources to the net has caused
a proliferation of free operating systems. Systems that you might well run into include:
AIX, IBMs name for its UNIX versions. Current versions are based on System V.3, but
IBM has stated an intent to migrate to OSF/1 (IBM is a leading member of the OSF).
Compared to System V, it has a large number of extensions, some of which can cause
significant pain to the unwary.
HP-UX, Hewlett Packards UNIX system. It is based on System V.3, but contains a large
number of so-called BSD extensions. Within HP, it is considered to be about 80% BSD-
compliant.
Linux, a UNIX clone for the Intel 386 architecture written by Linus Torvalds, a student in
Helsinki. It has absolutely no direct connection with traditional UNIX flavours, which
gives it the unique advantage amongst free UNIXes of not being a potential subject for
litigation. Apart from that, it has a vaguely System V-like feeling about it. If you are
porting to Linux, you should definitely subscribe to the very active network news groups
(comp.os.linux.*).
SunOS is the generic name of Sun Microsystems operating systems. The original
SunOS was derived from 4.2BSD and 4.3BSD, and until release 4.1 it was predominantly
BSD-based with a significant System V influence. Starting with version 5.0, it is a some-
what modified version of System V.4. These later versions are frequently referred to as
Solaris, though this term properly applies to the complete system environment, including
windowing system (OpenWindows), development tools and such, and does not apply
only to the System V based versions. Solaris 1.x includes the BSD-based SunOS 4.1 as
its kernel; Solaris 2.x includes the System V.4-based SunOS 5.x as its kernel.
Ultrix is DECs port of 4.1BSD and 4.2BSD to the VAX and MIPS-based workstations.
It is now obsolete and has been replaced by OSF/1.
I would have liked to go into more detail about these versions of UNIX, but doing so would
have increased the size of the book significantly, and even then it wouldnt be possible to
guarantee the accuracy: most systems add functionality in the course of their evolution, and
information that is valid for one release may not apply to an earlier or a later release. As a
result, Ive made a compromise: nearly all UNIX features were introduced either in BSD or
System V, so I will distinguish primarily between these two. Where significant differences
exist in other operating systemSunOS 4 is a good example I will discuss them separately.
Where does this leave you with, say, NonStop UX version B30? NonStop UX version B is a
version of UNIX System V.4 that runs on Tandems Integrity series of fault-tolerant MIPS-
based UNIX systems. It includes some additional functionality to manipulate the hardware,
and some of the header files differ from the standard System V.4. In addition, it includes a
minimal carry-over of BSDisms from the System V.3 version. Obviously, you can start by
treating it as an implementation of System V.4, but occasionally you will find things that dont
quite seem to fit in. Since its a MIPS-based system, you might try to consider it to be like
SGIs IRIX operating system version 5, which is System V.4 for SGIs MIPS-based hardware.
Indeed, most IRIX 5.x binaries will also run unchanged on NonStop UX version B, but you
will notice significant differences when you try to port packages that already run on IRIX 5.x.
These differences are typical of a port to just about every real-life system. There are very few
pure System V.4 or pure BSD systems out thereeverybody has added something to their
port. Ultimately, you will need to examine each individual problem as it occurs. Here is a
strategy you can use to untangle most problems on UNIX systems:
Interpret the error messages to figure out what feature or function call is causing the
problem. Typically, the error message will come from the compiler and will point to a
specific line in a specific file.
Look up the feature or call in this book. Use the description to figure out what the origi-
nal programmer intended it to do.
Figure out how to achieve the same effect on your own system. Sometimes, I recom-
mend a change which you can make and try the program again. If youre not sure how
your system works, you can probably find a manual page for the feature or call, and this
book will help you interpret it.
Apart from such possible dangers, there is very little that can go wrong. If you are building a
package that has already had been ported to your platform, you should not run into any prob-
lems that this book cant help you solve, even if you have negligible background in program-
ming and none in porting.
frequently ported packages. Well port them to an Intel 486DX/2-66 machine running
BSD/386 Version 1.1.*
Part 2
As long as things go smoothly, you can get through the kind of port described in the first part
of this book with little or no programming knowledge. Unfortunately, things dont always go
smoothly. If they dont, you may need to make possibly far-reaching changes to the sources.
Part 1 doesnt pay much attention to this kind of modificationthats the topic of part 2 of
this book, which does expect a good understanding of programming:
In Chapter 11, Hardware dependencies, well look at problems caused by differences in
the underlying hardware platform.
In the following five chapters, well look at some of the differences in different UNIX
flavours. First well look at a number of smaller differences in Chapter 12, Kernel
dependencies, then well look at some of the more common problem areas in Chapter 13,
Signals, Chapter 14, File systems, Chapter 15, Terminal drivers, and Chapter 16, Time-
keeping.
Well look at the surprising number of headaches caused by header files in Chapter 17,
Header files, and at system library functionality in Chapter 18, Function libraries.
Well examine the differences between various flavours of the more important tools in
Chapter 19, Make, Chapter 20, Compilers, and Chapter 21, Object files and friends.
Finally, there are a number of appendixes:
Appendix A, Comparative reference to UNIX data types, describes the plethora of data
types that have developed since the advent of ANSI C.
Appendix B, Compiler flags, gives you a comparative reference to the compiler flags of
many common systems.
Appendix C, Assembler directives and flags, gives you a comparative reference to assem-
bler directives and flags.
Appendix D, Linker flags, gives you a comparative reference to linker flags.
Appendix E, Where to get sources, gives you information on where to find useful source
files, including a number of the packages we discuss in this book.
* With the exception of Taylor uucp, BSD/OS, which at the time was called BSD/386, is supplied with
all these packages, so you would only be need to port them if you wanted to modify them or port a new
version.
Preparations
You dont need much to port most packages. Normally everything you needa C compiler, a
C library, make and some standard toolsshould be available on your system. If you have a
system that doesnt include some of these tools, such as a System V release where every indi-
vidual program seems to cost extra, or if the tools are so out-of-date that they are almost use-
less, such as XENIX, you may have problems.
If your tools are less than adequate, you should consider using the products of the Free Soft-
ware Foundation. In particular, the GNU C compiler gcc is better than many proprietary com-
pilers, and is the standard compiler of the Open Software Foundation. You can get many
packages directly from the Internet or on CD-ROM. If you are going to be doing any serious
porting, I recommend that you get at least the GNU software packages, 4.4BSD Lite, and
TEX, preferably on CD-ROM. In particular, the GNU software and 4.4BSD Lite contain the
sources to many library functions that may be missing from your system. In addition, many
of the GNU packages are available in precompiled binary form from a number of sources. Ill
refer to these packages frequently in the text.
* Of course, it should approach 7 kilobytes per second, but network congestion can pull this figure down
to a trickle.
13
Archives
You frequently get pure source trees on CD-ROM, but other media, and also many CD-ROMs,
transform the source tree several times:
A source tree is usually supplied in an archive, a file containing a number of other files.
Like a paper bag around groceries, an archive puts a wrapper around the files so that you
can handle them more easily. It does not save any space in fact, the wrapper makes it
slightly larger than the sum of its files.
Archives make it easier to handle files, but they dont do anything to save space. Much
of the information in files is redundant: each byte can have 256 different values, but typi-
cally 99% of an archive of text or program sources will consist of the 96 printable ASCII
characters, and a large proportion of these characters will be blanks. It makes sense to
encode them in a more efficient manner to save space. This is the purpose of compres-
sion programs. Modern compression programs such as gzip reduce the size of an archive
by up to 90%.
If you want to transfer archives by electronic mail, you may also need to encode them to
comply with the allowable email character set.
Large archives can become very unwieldy. We have already seen that it can take several
hours to transfer gcc. If the line drops in this time, you may find that you have to start
the file again. As a result, archives are frequently split into more manageable chunks.
The most common form of archive youll find on the Internet or on CD-ROM is gzipped tar, a
tar archive that has been compressed with gzip. A close second is compressed tar, a tar
* To quote a fortune from the fortune program: Never underestimate the bandwidth of a station wagon
full of tapes..
archive that has been compressed with compress. From time to time, youll find a number of
others. In the following sections well take a brief look at the programs that perform these
tasks and recover the data.
Archive programs
A number of archive programs are available:
tar, the tape archive program, is the all-time favourite. The chances are about 95% that
your archive will be in tar format, even if it has nothing to do with tape.
cpio is a newer file format that once, years ago, was intended to replace tar. cpio ar-
chives suffer from compatibility problems, however, and you dont see them very often.
ar is a disk archive program. It is occasionally used for source archives, though
nowadays it is almost only used for object file archives. The ar archive format has never
been completely standardized, so you get an ar archive from a different machine, you
might have a lot of trouble extracting it. Well look at ar formats again in , on page 383.
shar is the shell archive program. It is unique amongst archive programs in never using
non-printing characters, so shar archives can be sent by mail. You can extract shar ar-
chives simply by feeding them to a (Bourne) shell, though it is safer to use a program
like unshar.
Basic use
When it comes to unpacking software, one or two tar commands can meet all your needs.
First, you often want to look at the contents before unpacking. Assuming that the archive is
named et1.3.tar, the following command lists the files in the archive:
$ tar tf et1.3.tar
et1.3/
et1.3/bell.c
pet1.3/bltgraph.c
et1.3/BLURB
The t option stands for table of contents, and the f option means use the next parameter in
the command (et1.3.tar) as the name of the archive to list.
To read in the files that were listed, use the command:
$ tar xfv et1.3.tar
et1.3/
et1.3/bell.c
pet1.3/bltgraph.c
et1.3/BLURB
The list looks the same, but this time the command actually creates the directory et1.3 if nec-
essary, and then creates the contents. The x option stands for extract, and the f option has the
same meaning as before. The v option means verbose and is responsible for generating the
list, which gives you the assurance that the command is actually doing something.
To bundle some files into an archive, use a command like:
$ tar cvf et1.3.tar et1.3
This command packs everything in the et1.3 directory into an archive named et1.3.tar (which
is where we started). The c option stands for create and the v option for verbose. This
time, the f means use the next parameter in the command (et1.3.tar) as the archive to create.
Absolute pathnames
Many versions of tar have difficulties with absolute pathnames. If you back up a directory
/usr/foo, they will only be able to restore it to exactly this directory. If the directory is
/usr/bin, and youre trying to restore programs like sh, this could give you serious problems.
Some versions of tar have an option to ignore the leading /, and others, such as GNU tar,
ignore it unless you tell them otherwise.
Symbolic links
Many versions of tar will only back up a symbolic link, not the file or directory to which it
points. This can be very embarrassing if you send somebody a tape with what should be a
complete software package, and it arrives with only a single symbolic link.
Unfortunately, this can cause problems too. Some DDS drives cannot read tapes with block
sizes of more than 32768 bytes, and some versions of tar, such as SGI IRIS 5.x, cannot handle
tapes blocked larger than 20 tape blocks (10240 bytes). This is a show-stopper if you have a
tape which is really blocked at more than this size: you just wont be able to read it directly.
You can solve this problem by installing GNU tar or piping the archive through dd:
$ dd if=/dev/rmt/ctape0 ibs=128b obs=2b | tar xvf -
File names
Most versions of tar perform filename matching based on the exact text as it appears on the
tape. If you want to extract specific files, you must use the names by which they are known in
the archive. For example, some versions of tar may end up writing absolute names with two
leading slashes (like //usr/bin/sh, for example). This doesnt worry the operating system,
which treats multiple leading slashes the same as a single leading slash, but if you want to
This bug has been around so long that you might suspect that it is an insider joke. In fact, it
is a benign compatibility problem. The POSIX.2 standard tar format allows archives to con-
tain both directory and file names, although the directory names are not really necessary:
assuming it has permission, tar creates all directories necessary to extract a file. The only use
of the directory names is to specify the modification time and permissions of the directory.
Older versions of tar, including System V tar, do not include the directory names in the ar-
chive, and dont understand them when they find them. In this example, we have extracted a
POSIX.2 tar archive on a System V system, and it doesnt understand (or need) the directory
information. The only effect is that the directories will not have the correct modification time-
stamps and possibly not the correct permissions.
Multivolume archives
tar can also handle multi-volume archives, in other words archives that go over more than one
tape. The methods used are not completely portable: one version of tar may not be able to
read multivolume archives written by a different version. Some versions of tar just stop writ-
ing data at the end of one tape and continue where they left off at the beginning of the next
reel, whereas others write header information on the second tape to indicate that it is a contin-
uation volume. If possible, you should avoid writing multivolume archives unless you are
sure that the destination system can read them. If you run into problems with multivolume ar-
chives you cant read, you might save the day with something like:
$ (dd if=$TAPE
++ echo 1>&2 Change tapes and press RET
++ read confirmation the name of the variable isnt important
++ dd if=$TAPE
++ echo 1>&2 Change tapes and press RET
++ read confirmation
++ dd if=$TAPE) | tar xvf -
This uses dd to copy the first tape to stdout, then prints a message and waits for you to press
the enter key, copies a second tape, prompts and waits again, and then copies a third tape.
Since all the commands are in parentheses, the standard output of all three dd commands is
piped into the tar waiting outside. The echo commands need to go to stderr (thats the 1>&2)
to get displayed on the terminalotherwise they would be piped into the tar, which would
not appreciate it.
This only works if the version of tar you use doesnt put any header information (like reel
number and a repeat of the file header) at the beginning of the subsequent reels. If it does, and
you cant find a compatible tar to extract it again, the following method may help. Assuming
a user of an SCO system has given you a large program foo spread over 3 diskettes, each of
which contains header information that your tar doesnt understand, you might enter
$ tar x foo extract first part from first floppy
$ mv foo foo.0 save the first part
$ tar x foo extract second part from second floppy
$ mv foo foo.1 save the second part
$ tar x foo extract third part from third floppy
$ mv foo foo.2 save the third part
$ cat foo.* >foo concatenate them
$ rm foo.* and remove the intermediate files
Others, however, show the files from the viewpoint of the directory itselfthe directory name
is missing in the archive:
$ tar tvf blaster.tar
-rw-r--r-- 400/1 5666 Feb 14 01:44 1993 README
-rw-r--r-- 400/1 3638 Feb 14 01:44 1993 INSTALL
-r--r--r-- 400/1 2117 Feb 14 01:44 1993 LICENSE
-rw-r--r-- 400/1 2420 Feb 14 15:17 1993 Makefile
-rw-r--r-- 400/1 3408 Feb 14 01:44 1993 sb_asm.s
-rw------- 400/1 10247 Feb 14 01:44 1993 stream.c
-rw-r--r-- 400/1 1722 Feb 14 04:10 1993 apps/Makefile
If you have an archive like the first example, you want to be in the parent directory when you
extract the archive; in the second case you need to first create the directory and then cd to it.
If you extract the second archive while in the parent directory, you will face a lot of cleaning
up. In addition, there is a good chance that files with names like README, INSTALL and
LICENSE may already be present in that directory, and extracting this archive would over-
write them. There are a couple of ways to avoid these problems:
Always look at the archive contents with tar t before extracting it. Once you have looked
at the archive contents, you can change to the correct directory into which to extract it.
In the case of groff above, you might choose a directory name like /mysources*. In the
case of blaster, you could create a directory /mysources/blaster and extract into that
directory.
Alternatively, you can always create a subdirectory and extract there, and then rename
the directory. In the first example, you might create a directory /mysources/temp. After
extraction, you might find that the files were in a directory /mysources/temp/groff-1.09,
so you could move them with
$ mv groff-1.09 ..
If they extract directly into temp, you can rename the directory:
$ cd ..
$ mv temp groff-1.09
This method may seem easier, but in fact there are a couple of problems with it:
You need to choose a directory name that doesnt clash with the real name. Thats
why we used the name temp in this example: otherwise it wont be possible to
rename the directory in the first example, since you would be trying to overwrite the
directory with one of its own subdirectories.
* A number of shells use the shorthand notation / to refer to your home directory.
Frequently your tar archive will be compressed in some way. There are methods for extract-
ing files directly from compressed archives. Well examine these when we look at compres-
sion programs on page .
Compression programs
If the archive is compressed, you will need to uncompress it before you can extract files from
it. UNIX systems almost invariably use one of three compression formats:
compressed files are created with the compress program and extracted with uncompress.
They can be up to 70% smaller than the original file. The zcat program will uncompress
a compressed file to the standard output.
gzipped files are created by gzip and extracted by gunzip. They can be up to 90%
smaller than the original file. gunzip will also uncompress compressed or packed files.
packed files are obsolete, though you still occasionally see packed man pages. They are
created by the pack program and uncompressed by the unpack program. The pcat pro-
gram will uncompress a packed file to the standard output.
Each of these programs is installed with three different names. The name determines the
behavior. For example, gzip is also known as gunzip and zcat:
$ ls -li /opt/bin/gzip /opt/bin/gunzip /opt/bin/zcat
13982 -rwxr-xr-x 3 grog wheel 77824 Nov 5 1993 /opt/bin/gunzip
13982 -rwxr-xr-x 3 grog wheel 77824 Nov 5 1993 /opt/bin/gzip
13982 -rwxr-xr-x 3 grog wheel 77824 Nov 5 1993 /opt/bin/zcat
The -i option to ls tells it to list the inode number, which uniquely identifies the file. In this
case, you will see that all three names are linked to the same file (and that the link count field
is 3 as a result). You will notice that gzip has also been installed under then name zcat,
replacing the name used by compress. This is not a problem, since gzcat can do everything
that zcat can do, but it can lead to confusion if you rely on it and one day try to extract a
gzipped file with the real zcat.
Encoded files
Most archive programs and all compression programs produce output containing non-print-
able characters. This can be a problem if you want to transfer the archive via electronic mail,
which cannot handle all binary combinations. To solve this problem, the files can be encoded:
they are transformed into a representation that contains only printable characters. This has the
disadvantage that it makes the file significantly larger, so it is used only when absolutely
Split archives
Many ftp sites split large archives into equal-sized chunks, typically between 256 kB and 1.44
MB (a floppy disk image). Its trivial to combine them back to the original archive: cat will
do just that. For example, if you have a set of files base09.000 through base09.013 represent-
ing a gzipped tar archive, you can combine them with:
$ cat base09.* > base09.tar.gz
This will, of course, require twice the amount of storage, and it takes time. Its easier to
extract them directly:
$ cat base09.* | gunzip | tar xvf -
drwxr-xr-x root/wheel 0 Aug 23 06:22 1993 ./sbin/
-r-xr-xr-x bin/bin 106496 Aug 23 06:21 1993 ./sbin/chown
-r-xr-xr-x bin/bin 53248 Aug 23 06:21 1993 ./sbin/mount_mfs
... etc
cat pipes all archives in alphabetical file name order to gunzip. gunzip uncompresses it and
pipes the uncompressed data to tar, which extracts the files.
Name Format
suffix
# Alternate patch reject file name.
emacs backup files, also used by some versions of patch.
,v RCS file. Created by ci, extracted by co.
.a ar format. Created by and extracted with ar.
.arc Created by and extracted with arc.
.arj DOS arj format
.cpio Created by and extracted with cpio.
.diff Difference file, created by diff, can be applied by patch.
.gif Graphics Interchange Format
.gz gzip format. Created by gzip, extracted with gunzip.
.hqx HQX (Apple Macintosh)
.jpg JPEG (graphics format)
.lzh LHa, LHarc, Larc
.orig Original file after processing by patch.
.rej patch reject file.
.shar Shell archive: created by shar, extracted with any Bourne-compatible shell.
.sit Stuff-It (Apple Macintosh)
.tar tar format. Created by and extracted with tar.
.uu uuencoded file. Created by uuencode, decoded with uudecode.
* If you have one of these systems, and you have a choice of file systems, you can save yourself a lot of
trouble by installing one that allows long file names.
Identifying archives
Occasionally youll get an archive whose name gives you no indication of the format. Under
these circumstances, finding the kind of archive can be a matter of trial and error, particularly
if it is compressed. Here are a couple of ideas that might help:
file
The UNIX file command recognizes a lot of standard file types and prints a brief description
of the format. Unfortunately, the file really needs to be a file: file performs some file system
checks, so it cant read from standard input. For example,
$ file *
0install.txt: English text
base09.000: gzip compressed data - deflate method , original
file name , last modified: Mon Aug 23 07:53:21 1993 , max compression os:
Unix
base09.001: data
...more of same
base09.011: DOS executable (COM)
man-1.0.cpio: cpio archive
tcl7.3.tar.gz: empty
tex: directory
tk3.6.tar: POSIX tar archive
The information for base09.000 was one output line that wrapped around onto 3 output lines.
Most files have certain special values, so-called magic numbers, in specific locations in their
headers. file uses a file, usually /etc/magic, which describes these formats. Occasionally it
makes a mistakewe can be reasonably sure that the file base09.011 is not a DOS
executable, but it has the right number in the right place, and thus fools file.
This version of file (from BSD/OS) recognizes base09.000and none of the following pieces
of the archive as a gzip archive file, and even extracts a lot of information. Not all versions
of file do this. Frequently, it just tells you that the archive is data in this case, the first
assumption should be that the archive is compressed in a format that your version of file
doesnt recognize. If the file is packed, compressed or gzipped, gzip expands it, and otherwise
it prints an error message, so the next step might look something like:
In this case, we have established that the file mystery is, in fact, a compressed tar archive,
though we dont know what kind of compression, since gzip doesnt tell.
If file tells you that the file is ASCII or English text, then you can safely look at it with more
or less:
$ more strange-file
Newsgroups: comp.sources.unix
From: [email protected] (Chris Lewis)
Subject: v26i014: psroff 3.0, Patch09
Sender: [email protected]
Approved: [email protected]
Patchwrapped: 920128230528
Index: ./lib/lj3.fonts
*** /tmp/PATCHold/./lib/lj3.fonts Tue Jan 28 23:03:45 1992
--- ./lib/lj3.fonts Tue Jan 28 23:03:46 1992
This is a plain text patch file: you can pass it straight through the patch program, since patch
doesnt worry about junk at the beginning or the end of the file. Well look at patch in depth
in Chapter 3, Care and feeding of source trees, page 30.
Newsgroups: comp.sources.unix From: [email protected] (Larry McVoy)
Subject: v26i020: perfmon - interface to rstatd(8)
Sender: [email protected]
Approved: [email protected] ... more stuff omitted
#! /bin/sh
# This is a shell archive. Remove anything before this line,
# then unpack it by saving it into a file and typing "sh file".
As the text tells you, this is a shell archive. To extract it, you can remove all text up to the line
starting with #!/bin/sh and extract it with the Bourne shell, or pass it through unshar as it is.
begin 666 magic.gz
MXL("_!NRTV5A<W1E<@!-4KV.VS,WO,4W(N;:\9:B+3)T.*1HT*DH
M<+3$V+I(HB*2?/V)14W=YMED-\OGW8HE0K0.#[![V/A!4B<(M4_>1C>ZTS
MNW&$:<D5>!J9_(0\@:@C?SJ#SU@]IP7V&4L6V=TOAF?Y[N%C#U\@D0B.
M!%/PGK+NV[)A\/!*KH)C3[:,!<>"R9T<<KGZC3Z4K9*VUE&B.O"C?H&Q4
MA+,8C"(I2&&/((7&H?![;JX4O0?X]$Y)!\HR3\%U.FT(TE#I>#0YE$*M
MU$C>%#UPT>&L?WY\ZQKNU_[_S</SN@1226061"15.!K);DF4#4RHFD7
M2;/R8BI/=)5:U*1TMG\W>C=O0PJF]N:(U[L45\B*NIIGPDN%..49+$T%8
MXA7>ZEWS"B;<\3+%O30(.%[%8)TK&<I/O6[6\!M>TPDM"U1+Y3%NXA#K!
M28*%RR?MZKA6:NWI5L?&&UM7I1>8,(S05K<!(D+44<N&E$R;OKD%#7!-P
M<?66PQR.R73X>E,D0U_"QFUP@YFCJ$&IVST=)2L0:-OH%(QNHF:MMI$>O8
I3#PH#VM<#H4>_]<O$)*>PYU)JPJE7>;*:>5!)4S]9O,/(PQ?IS4#!I
end
This is a uuencoded file. The first line contains the word begin, the default security (which
you cant change) and the name of the archive (magic.gz). The following lines usually have
the same length and begin with the same letter (usually M)this is the encoded length specifi-
cation for the line. If they dont, something has probably gone wrong in the transmission.
The last data line is usually shorter, and thus has a different first character. Finally, the archive
contains two end lines: the first is usually the single character , and the second is the word
end on a line by itself.
To extract the file, first pass it through uudecode, which will create the file magic.gz, then gun-
zip it to create the file magic. Then you might need to use file to find out what it is.
$ uudecode < magic.uue
$ gunzip magic.gz
$ file magic
magic: English text
This is a btoa encoded file, probably also gzipped like the previous example. Extract it with
btoa -a and then proceed as with uuencoded files.
README
By convention, many authors include a file README in the main directory of the package.
README should tell you at least:
The GNU termcap library does not place an arbitrary limit on the size of termcap
entries, unlike most other termcap libraries.
In some cases, however, there doesnt seem to be any file to tell you what the package does.
Sometimes you may be lucky and find a good man page or even documentation intended to be
printed as hardcopysee Chapter 7, Documentation for more information. In many cases,
though, you might be justified in deciding that the package is so badly documented that you
give up.
There may also be files with names like README.BSD, README.SYSV, README.X11 and
such. If present, these will usually give specific advice to people using these platforms.
INSTALL file
There may be a separate INSTALL file, or the information it should contain might be included
in the README file. It should tell you:
A list of the platforms on which the package has been ported. This list may or may not
include your system, but either way it should give you a first inkling of the effort that lies
in store. If youre running System V.4, for example, and it has already been ported to
your hardware platform running System V.3, then it should be easy. If it has been ported
to V.4, and youre running V.3, this can be a completely different matter.
A description of how to configure the package (well look at this in Chapter 4, Package
configuration).
A description of how to build the package (see Chapter 4, Package configuration and
Chapter 19, Make for more details on this subject).
It may, in addition, offer suggestions on how to port to other platforms or architectures.
Other files
The package may include other information files as well. By convention, the names are writ-
ten in upper case or with an initial capital letter, so that they will be stand out in a directory
listing. The GNU project software may include some or all of the following files:
ABOUT is an alternative name used instead of README by some authors.
COPYING and COPYING.LIB are legal texts describing the constraints under which you
may use the software.
ChangeLog is a list of changes to the software. This name is hard-coded into the emacs
editor macros, so its a good chance that a file with this name will really be an emacs-
style change log.
MANIFEST may give you a list of the files intended to be in the package.
PROBLEMS may help you if you run into problems.
SERVICE is supplied by the Free Software Foundation to point you to companies and
individuals who can help you if you run into trouble.
A good example of these files is the root directory of Taylor uucp:
$ gunzip </cd0/gnu/uucp/uucp-1.05.tar.gz |tar tvf -
drwxrwxr-x 269/15 0 May 6 06:10 1994 uucp-1.05/
-r--r--r-- 269/15 17976 May 6 05:23 1994 uucp-1.05/COPYING
-r--r--r-- 269/15 163997 May 6 05:24 1994 uucp-1.05/ChangeLog
C$
This archive adheres to the GNU convention of including the name of the top-level directory
in the archive. When we extract the archive, tar will create a new directory uucp-1.05 and put
all the files in it. So we continue:
$ cd /porting/src the directory in which I do my porting
$ gunzip </cd0/gnu/uucp/uucp-1.05.tar.gz |tar xf -
$
After extraction, the resultant directory contains most of the standard files that we discussed
above:
$ cd uucp-1.05
$ ls -l
total 1724
drwxrwxr-x 7 grog wheel 1536 May 6 06:10 .
drwxrwxrwx 44 grog wheel 3584 Aug 19 14:34 ..
-r--r--r-- 1 grog wheel 17976 May 6 05:23 COPYING
-r--r--r-- 1 grog wheel 163997 May 6 05:24 ChangeLog
-r--r--r-- 1 grog wheel 499 May 6 05:24 MANIFEST
-rw-r--r-- 1 grog wheel 14452 May 6 06:09 Makefile.in
-r--r--r-- 1 grog wheel 4283 May 6 05:24 NEWS
-r--r--r-- 1 grog wheel 7744 May 6 05:24 README
-r--r--r-- 1 grog wheel 23563 May 6 05:24 TODO
-r--r--r-- 1 grog wheel 32866 May 6 05:24 chat.c
29
Where to go from here, page 144. In our case study, we have gcc version 2.5.6 and want to
update to 2.5.8. We discover the following files on the file server:
ftp> ls
200 PORT command successful.
150 Opening ASCII mode data connection for /bin/ls.
-rw-rw-r-- 1 117 1001 10753 Dec 12 19:15 gcc-2.5.6-2.5.7.diff.gz
-rw-rw-r-- 1 117 1001 14726 Jan 24 09:02 gcc-2.5.7-2.5.8.diff.gz
-rw-rw-r-- 1 117 1001 5955006 Dec 22 14:16 gcc-2.5.7.tar.gz
-rw-rw-r-- 1 117 1001 5997896 Jan 24 09:03 gcc-2.5.8.tar.gz
226 Transfer complete.
ftp>
In other words, we have the choice of copying the two diff files gcc-2.5.6-2.5.7.diff.gz and
gcc-2.5.7-2.5.8.diff.gz, a total of 25 kB, and applying them to your source tree, or copying the
complete 6 MB archive gcc-2.5.8.tar.gz.
Patch
diff files are reasonably understandable, and you can apply the patches by hand if you want,
but its obviously easier and safer to use a program to apply the changes. This is the purpose
of patch. patch takes the output of the program diff and uses it to update one or more files. To
apply the patch, it proceeds as follows:
1. First, it looks for a file header. If it finds any junk before the file header, it skips it and
prints a message to say that it has done so. It uses the file header to recognize the kind of
diff to apply.
2. It renames the old file by appending a string to its name. By default, the string is .orig,
so foo.c would become foo.c.orig.
3. It then creates a new file with the name of the old file, and copies the old file to the new
file, modifying it with the patches as it goes. Each set of changes is called a hunk.
The way patch applies the patch depends on the format. The most dangerous kind are ed style
diffs, because there is no way to be sure that the text is being replaced correctly. With context
diffs, it can check that the context is correct, and will look a couple of lines in each direction
if it doesnt find the old text where it expects it. You can set the number of lines it will look
(the fuzz factor) with the -F flag. It defaults to 2.
If the old version of the file does not correspond exactly to the old version used to make the
diff, patch may not be able to find the correct place to insert the patch. Except for ed format
diffs, it will recognize when this happens, and will print an error message and move the corre-
sponding hunk to a file with the suffix .rej (for reject).
A typical example are the patches for X11R5. You might start with the sources supplied on
the companion CD-ROM to X Window System Administrators Guide by Linda Mui and Eric
Pearce. This CD-ROM includes the complete X11R5 sources to patch level 21. At the time
of writing, five further patches to X11R5 have been released. To bring the source tree up to
patch level 26, you would proceed as follows:
First, read the header of the patch file. As we have seen, patch allows text before the first file
header, and the headers frequently contain useful information. Looking at patch 22, we see:
$ gunzip < /cd0/x11r5/fix22.gz | more
X11 R5 Public Patch #22
MIT X Consortium
cd to the top of the source tree (to the directory containing the
"mit" and "contrib" subdirectories) and do:
Patch works silently unless an error occurs. You are likely to get the
following warning messages, which you can ignore:
In this example we have used gunzip to look at the file directly; we could just as well have
used GNU zcat. The patch header suggests the flags -s and -p. The -s flag to patch tells it
to perform its work silentlyotherwise it prints out lots of information about what it is doing
and why. The -p flag is one of the most complicated to use: it specifies the pathname strip
count, how to treat the directory part of the file names in the header. Well look at it in more
detail in the section Cant find file to patch on page 36.
This information is important: patch is rather like a chainsaw without a guard, and if you start
it without knowing what you are doing, you can make a real mess of its environment. In this
case, we should find that the root of our source tree looks like:
$ cd /usr/x11r5
$ ls -FC mit
Imakefile RELNOTES.ms extensions/ rgb/
LABEL bug-report fonts/ server/
Makefile clients/ hardcopy/ util/
Makefile.ini config/ include/
RELNOTES.PS demos/ lib/
RELNOTES.TXT doc/ man/
... that looks OK, were in the right place
$ gunzip < /cd0/x11r5/fix22.gz | patch -p -s
Weve taken another liberty in this example: since the patch file was on CD-ROM in com-
pressed form, we would have needed to extract it to disk in order to patch the way the file
header suggests. Instead, we just gunzip directly into the patch program.
Its easy to make mistakes when patching. If you try to apply a patch twice, patch will notice,
but you can persuade it to reapply the patch anyway. In this section, well look at the havoc
that can occur as a result. In addition, well disregard some of the advice in the patch header.
This is the way I prefer to do it:
$ gunzip < /cd0/x11r5/fix23.gz | patch -p &> patch.log
This invocation allows patch to say what it has to say (no -s flag), but copies both the stan-
dard output and the error output to the file patch.log, so nothing appears on the screen. You
can, of course, pipe the output through the tee program, but in practice things happen so fast
that any error message will usually run off the screen before you can read it. It certainly
would have done so here: patch.log had a length of 930 lines. It starts with
Hmm... Looks like a new-style context diff to me...
The text leading up to this was:
--------------------------
| Release 5 Public Patch #23
| MIT X Consortium
... followed by the complete header
|Prereq: public-patch-22
This last line is one safeguard that patch offers to ensure that you are working with the correct
source tree. If patch finds a Prereq: line in the file header, it checks that this text appears in
the input file. For comparison, heres the header of mit/bug-report:
To: [email protected]
Subject: [area]: [synopsis] [replace with actual area and short description]
VERSION:
R5, public-patch-22
[MIT public patches will edit this line to indicate the patch level]
In this case, patch finds the text. When it does, it prints out the corresponding message:
|
|*** /tmp/,RCSt1006225 Tue Mar 9 14:40:48 1993
|--- mit/bug-report Tue Mar 9 14:37:04 1993
--------------------------
Good. This file appears to be the public-patch-22 version.
This message shows that it has found the text in mit/bug-report. The first hunk in any X11 diff
changes this text (in this case to public-patch-23), so that it will notice a repeated application
of the patch. Continuing,
Patching file mit/bug-report using Plan A...
Hunk #1 succeeded at 2.
Hmm... The next patch looks like a new-style context diff to me...
The text leading up to this was:
--------------------------
|*** /tmp/,RCSt1005203 Tue Mar 9 13:45:42 1993
|--- mit/lib/X/Imakefile Tue Mar 9 13:45:45 1993
--------------------------
Patching file mit/lib/X/Imakefile using Plan A...
Hunk #1 succeeded at 1.
Hunk #2 succeeded at 856.
Hunk #3 succeeded at 883.
Hunk #4 succeeded at 891.
Hunk #5 succeeded at 929.
Hunk #6 succeeded at 943.
Hunk #7 succeeded at 968.
Hunk #8 succeeded at 976.
Hmm... The next patch looks like a new-style context diff to me...
This output goes on for hundreds of lines. What happens if you make a mistake and try
again?
$ gunzip < /cd0/x11r5/fix23.gz | patch -p &> patch.log
This file doesnt appear to be the public-patch-22 version--patch anyway? [n] y
bad choice...
Reversed (or previously applied) patch detected! Assume -R? [y] RETURN pressed
Reversed (or previously applied) patch detected! Assume -R? [y] RETURN pressed
Reversed (or previously applied) patch detected! Assume -R? [y] C$
The first message is printed because patch didnt find the text public-patch-22 in the file
(in the previous step, patch changed it to read public-patch-23). This message also
appears in patch.log. Of course, in any normal application you should immediately stop and
check whats gone wrong. In this case, I make the incorrect choice and go ahead with the
patch. Worse still, I entered RETURN to the next two prompts. Finally, I came to my senses
and hit CTRL-C, the interrupt character on my machine, to stop patch.
The result of this is that patch removed the patches in the first two files (the -R flag tells patch
to behave as if the files were reversed, which has the same effect as removing already applied
patches). I now have the first two files patched to patch level 22, and the others patched to
patch level 23. Clearly, I cant leave things like this.
Two wrongs dont normally make a right, but in this case they do. We do it again, and what
we get this time looks pretty much the same as the time before:
$ gunzip < /cd0/x11r5/fix23.gz | patch -p &> mit/patch.log
Reversed (or previously applied) patch detected! Assume -R? [y] C$
In fact, this time things went right, as we can see by looking at patch.log:
|*** /tmp/,RCSt1006225 Tue Mar 9 14:40:48 1993
|--- mit/bug-report Tue Mar 9 14:37:04 1993
--------------------------
Good. This file appears to be the public-patch-22 version.
Patching file mit/bug-report using Plan A...
Hunk #1 succeeded at 2.
Hmm... The next patch looks like a new-style context diff to me...
The text leading up to this was:
--------------------------
|*** /tmp/,RCSt1005203 Tue Mar 9 13:45:42 1993
|--- mit/lib/X/Imakefile Tue Mar 9 13:45:45 1993
--------------------------
Patching file mit/lib/X/Imakefile using Plan A...
Hunk #1 succeeded at 1.
(lots of hunks succeed)
Hmm... The next patch looks like a new-style context diff to me...
The text leading up to this was:
--------------------------
|*** /tmp/d03300 Tue Mar 9 09:16:46 1993
|--- mit/lib/X/Ximp/XimpLCUtil.c Tue Mar 9 09:16:41 1993
--------------------------
Patching file mit/lib/X/Ximp/XimpLCUtil.c using Plan A...
Reversed (or previously applied) patch detected! Assume -R? [y]
This time the first two files have been patched back to patch level 23, and we stop before
Hunk #3 failed
Patch makes an implicit assumption that the patch was created from an identical source tree.
This is not always the caseyou may have changed something in the course of the port. The
differences frequently dont cause problems if they are an area unrelated to the patch. In this
example, well look at how things can go wrong. Lets consider the following situation: dur-
ing a previous port of X11R5 pl 22,* you ran into some problems in mit/lib/Xt/Selection.c and
fixed them. The original text read:
if (XtWindow(widget) == window)
XtAddEventHandler(widget, mask, TRUE, proc, closure);
else {
Widget w = XtWindowToWidget(dpy, window);
RequestWindowRec *requestWindowRec;
if (w != NULL && w != widget) widget = w;
if (selectWindowContext == 0)
selectWindowContext = XUniqueContext();
You had problems with this section, so you commented out a couple of lines:
if (XtWindow(widget) == window)
XtAddEventHandler(widget, mask, TRUE, proc, closure);
else {
/* This doesnt make any sense at all - ignore
* Widget w = XtWindowToWidget(dpy, window); */
RequestWindowRec *requestWindowRec;
/* if (w != NULL && w != widget) widget = w; */
if (selectWindowContext == 0)
selectWindowContext = XUniqueContext();
What does this mean? Theres nothing for it but to look at the files concerned. In fix24 we
find
* The abbreviation pl is frequently used to mean patch level.
/***********************************************************
Copyright 1987, 1988 by Digital Equipment Corporation, Maynard, Massachusetts,
--- 1,4 ----
! /* $XConsortium: Selection.c,v 1.78 93/05/13 11:09:15 converse Exp $ */
/***********************************************************
Copyright 1987, 1988 by Digital Equipment Corporation, Maynard, Massachusetts,
***************
*** 70,75 ****
--- 70,90 ----
this must be hunk 2
Widget w; /* unused */
***************
*** 346,359 ****
and this must be hunk 3, the one that failed
{
Display *dpy = req->ctx->dpy;
Window window = req->requestor;
! Widget widget = req->widget;
... etc
***************
*** 1068,1073 ****
--- 1084,1096 ----
hunk 4
***************
*** 1176,1181 ****
--- 1199,1213 ----
and hunk 5--at least the count is correct
if (XtWindow(widget) == window)
! XtAddEventHandler(widget, mask, TRUE, proc, closure);
else {
- Widget w = XtWindowToWidget(dpy, window);
RequestWindowRec *requestWindowRec;
- if (w != NULL && w != widget) widget = w;
if (selectWindowContext == 0)
selectWindowContext = XUniqueContext();
if (XFindContext(dpy, window, selectWindowContext,
The characters + and - at the beginning of the lines in this hunk identify it as a unified context
diff. Well look at them in more detail in Chapter 10, Where to go from here, page 147. Not
surprisingly, they are the contents of hunk 3. Because of our fix, patch couldnt find the old
text and thus couldnt process this hunk. In this case, the easiest thing to do is to perform the
fix by hand. To do so, we need to look at the partially fixed file that patch created,
mit/lib/Xt/Selection.c. The line numbers have changed, of course, but since hunk 3 wasnt
applied, we find exactly the same text as in mit/lib/Xt/Selection.c.orig, only now it starts at
line 366. We can effectively replace it by the after text in Selection.c.rej, remembering of
course to remove the indicator characters in column 1.
One of the weaknesses of the combination of diff and patch is that its easy to get the file
names out of sync. What has probably happened here is that the file names dont agree with
your source tree. There are a number of ways for this to go wrong. The way that patch treats
the file names in diff headers depends on the -p flag, the so-called pathname strip count:
If you omit the -p flag, patch strips all directory name information from the file names
and leaves just the filename part. Consider the following diff header:
*** config/sunos4.h Wed Feb 29 07:13:57 1992
--- config/sunos4.h Mon May 17 18:19:56 1993
Relative to the top of the source tree, the file is in the directory config. If you omit the -p
flag, patch will look for the file sunos4.h, not config/sunos4.h, and will not find it.
If you specify -p, patch keeps the complete names in the headers.
If you specify -pn, patch will remove the first n directory name components in the path-
name. This is useful when the diffs contain incorrect base path names. For example, you
This kind of diff is a real nuisance: you at least need to search for the file sunos4.h, and if
youre unlucky youll find more than one and have to examine the patches to figure out which
one is intended. Then you need to give this name to the prompt, and patch should perform the
patches. Unfortunately, in a large collection of diffs, this can happen dozens of times.
Some versions of patch dont understand unified diffs, and since all versions skip anything
they dont understand, this could be the result. The only thing for it is to get a newer version
of patchsee Appendix E, Where to get sources, for details.
Malformed patch
If patch finds the files and understands the headers, you could still run into problems. One of
the most common is really a problem in making the diffs:
$ patch <diffs
Hmm... Looks like a unified diff to me...
The text leading up to this was:
--------------------------
Well, it tells you what happened: diff will print this message if the last character in a file is not
\n. Most versions of patch dont like the message. You need to edit the diff and remove the
offending line.
* To quote the beginning of the file: Someone sent this in from California, and we decided to extend our
campaign against information hoarding to recipes as well as software. (Recipes are the closest thing,
not involving computers, to software.)
This is a rather extreme example, but you might find any of the following in overgrown trees:
Old objects, editor backups and core dumps from previous builds. They may or may not
go away with a make clean.
Test programs left behind by somebody trying to get the thing to work on his platform.
These probably will not go away with a make clean.
Formatted documentation. Although the Makefile should treat documents like objects
when cleaning the tree, a surprising number of packages format and install documenta-
tion, and then forget about it when it comes to tidying it away again.
Old mail messages, only possibly related to the package. I dont know why this is, but
mail messages seem to be the last thing anybody wants to remove, and so they continue
to exist for years in many trees. This problem seems to be worse in proprietary packages
than in free packages.
The old objects are definitely the worst problem: make cant tell that they dont belong to this
configuration, and so they just prevent the correct version of the object being built. Depend-
ing on how different the architectures are, you may even find that the bogus objects fool the
linker, too, and you run into bizarre problems when you try to execute.
Link trees
You can simulate a writeable tree on disk by creating symbolic links to the sources on CD-
ROM. This way, the sources remain on the CD-ROM, but the objects get written to disk.
From your viewpoint, it looks as if all the files are in the same directory. For example, assume
you have a CD-ROM with a directory /cd0/src/find containing the sources to find:
$ ls -FC /cd0/src/find
COPYING Makefile config.status* lib/
COPYING.LIB Makefile.in configure* locate/
ChangeLog NEWS configure.in man/
The / at the end of the file names indicate that these files are directories; the * indicates that
they are executables. You could create a link tree with the following commands:
$ cd /home/mysrc/find put the links here
$ for i in /cd0/src/find/*; do
> ln -s $i .
> done
$ ls -l see what we got
total 16
lrwxrwxrwx COPYING -> /cd0/src/find/COPYING
lrwxrwxrwx COPYING.LIB -> /cd0/src/find/COPYING.LIB
lrwxrwxrwx ChangeLog -> /cd0/src/find/ChangeLog
lrwxrwxrwx INSTALL -> /cd0/src/find/INSTALL
lrwxrwxrwx Makefile -> /cd0/src/find/Makefile
lrwxrwxrwx Makefile.in -> /cd0/src/find/Makefile.in
lrwxrwxrwx NEWS -> /cd0/src/find/NEWS
lrwxrwxrwx README -> /cd0/src/find/README
lrwxrwxrwx config.status -> /cd0/src/find/config.status
lrwxrwxrwx configure -> /cd0/src/find/configure
lrwxrwxrwx configure.in -> /cd0/src/find/configure.in
lrwxrwxrwx find -> /cd0/src/find/find
lrwxrwxrwx lib -> /cd0/src/find/lib
lrwxrwxrwx locate -> /cd0/src/find/locate
lrwxrwxrwx man -> /cd0/src/find/man
lrwxrwxrwx xargs -> /cd0/src/find/xargs
I omitted most of the information that is printed by ls -l in order to get the information on the
page: what interests us here is that all the files, including the directories, are symbolic links.
In some cases, this is what we want: we dont need to create copies of the directories on the
hard disk when a single link to a directory on the CD-ROM does it just as well. In this case,
unfortunately, thats not the way it is: our sources are in the directory find, and thats where we
will have to write our objects. We need to do the whole thing again for the subdirectory find:
$ cd mysource/find change to the source directory on disk
$ rm find get rid of the directory symlink
$ mkdir find and make a directory
$ cd find and change to it
$ for i in /cd0/src/find/find/*; do
> ln -s $i .
> done
$ ls -l
total 18
lrwxrwxrwx Makefile -> /cd0/src/find/find/Makefile
lrwxrwxrwx Makefile.in -> /cd0/src/find/find/Makefile.in
lrwxrwxrwx defs.h -> /cd0/src/find/find/defs.h
lrwxrwxrwx find -> /cd0/src/find/find/find
lrwxrwxrwx find.c -> /cd0/src/find/find/find.c
lrwxrwxrwx fstype.c -> /cd0/src/find/find/fstype.c
lrwxrwxrwx parser.c -> /cd0/src/find/find/parser.c
lrwxrwxrwx pred.c -> /cd0/src/find/find/pred.c
lrwxrwxrwx tree.c -> /cd0/src/find/find/tree.c
Yes, this tree really does have a directory called find/find/find, but we dont need to worry
about it. Our sources and our Makefile are here. We should now be able to move back to the
top-level directory and perform the make:
$ cd ..
$ make
This is a relatively simple example, but it shows two important aspects of the technique:
You dont need to create a symlink for every single file. Although symlinks are relatively
small in this case, less than 100 bytesthey occupy up to 1024 bytes of disk space per
link, and you can easily find yourself taking up a megabyte of space just for the links.
On the other hand, you do need to make all the directories where output from the build
process is stored. You need to make symlinks to the existing files in these directories.
An additional problem with this technique is that many tools dont test whether they have suc-
ceeded in creating their output files. If they try to create files on CD-ROM and dont notice
that they have failed, you may get some strange and misleading error messages later on.
All you have to do in this case is to create a directory called /usr/obj/usr.bin/find. The Make-
files are set up to compile into that directory.
Timestamps
Its easy enough to recognize files that have been added to the source tree since its creation:
since they are all newer than any file in the original source tree, the simple command ls -lt
(probably piped into more or less) will display them in the reverse order in which they were
created (newest first) and thus separate the new from the old.
Every UNIX file and directory has three timestamps. The file system represents timestamps
in the time_t format, the number of seconds elapsed since January 1, 1970 UTC. See Chap-
ter 16, Timekeeping, page 270, for more details. The timestamps are:
The last modification timestamp, updated every time the file or directory is modified.
This is what most users think of as the file timestamp. You can display it with the ls -l
command.
The last access timestamp, updated every time a data transfer is made to or from the file.
You can display it with the ls -lu command. This timestamp can be useful in a number of
different places.
The status change timestamp (at least, thats what my header files call it). This is a sort
of kludged last modification timestamp for the inode, that part of a file which stores
information about the file. The most frequent changes which dont affect the other time-
stamps are change in the number of links or the permissions, which normally isnt much
use to anybody. On the other hand, the inode also contains the other timestamps, so if
this rule were enforced rigidly, a change to another timestamp would also change the sta-
tus change timestamp. This would make it almost completely useless. As a result, most
implementations suppress the change to the status change timestamp if only the other
timestamps are modified. If you want, you can display the status change timestamp with
the ls -lc command.
Whichever timestamp you choose to display with ls -l, you can cause ls to sort by it with the
-t flag. Thus, ls -lut displays and sorts by the last access timestamp.
Of these three timestamps, the last modification timestamp is by far the most important.
There are a number of reasons for this:
* To be pedantic, usually the assembler creates the object files, not the compiler.
A kludge is a programming short cut, usually a nasty, untidy one. The New Hackers Dictionary goes
to a lot of detail to explain the term, including why it should be spelt kluge and not kludge.
make relies on the last modification timestamp to decide what it needs to compile. If you
move the contents of a directory with cp, it changes all the modification timestamps to
the time when the copy was performed. If you then type make, you will perform a sig-
nificant amount of needless compilation.
Its frequently important to establish if two files are in fact the same, in other words, if
they have identical content. In the next section well see some programmatic tools that
help us with this, but as a first approximation we can assume that two files with the same
name, length and modification timestamp have an identical content, too. The modifica-
tion timestamp is the most important of these three: you can change the name, but if
length and timestamp are the same, theres still a good chance its the same file. If you
change the timestamp, you cant rely on the two files being the same just because they
have the same name and length.
As we have seen above, the last modification timestamp is useful for sorting when you
list directories. If youre looking for a file you made the week before last, it helps if it is
dated accordingly.
cmp
A modification timestamp isnt infallible, of course: even if EOF, timestamp and name are
identical, there still can be a lingering doubt as to whether the files really are identical. This
doubt becomes more pronounced if you seee something like:
$ ls -l
total 503
-rw-rw-rw- 1 grog wheel 1326 May 1 01:00 a29k-pinsn.c
-rw-rw-rw- 1 grog wheel 28871 May 1 01:00 a29k-tdep.c
-rw-rw-rw- 1 grog wheel 4259 May 1 01:00 a68v-nat.c
-rw-rw-rw- 1 grog wheel 4515 May 1 01:00 alpha-nat.c
-rw-rw-rw- 1 grog wheel 33690 May 1 01:00 alpha-tdep.c
... etc
Its a fairly clear bet that somebody has done a touch on all the files, and their modification
timestamps have all been set to midnight on May 1.* The cmp program can give you certainty:
$ cmp foo.c ../orig/foo.c compare with the original
$ echo $? show exit status
0 0: all OK
$ cmp bar.c ../orig/bar.c
bar.c ../orig/bar.c differ: char 1293, line 39
$ echo $? show exit status
1 1: they differ
Remember you can tell the shell to display the exit status of the previous command with the
shell variable $?. In the C shell, the corresponding variable is called $status. If the con-
tents of the files are identical, cmp says nothing and returns an exit status 0. If they are, it tells
you where they differ and returns 1. You can use the exit status in a shell script. For example,
the following Bourne shell script (it doesnt work with csh) compares files that are in both the
current tree (which is the current working directory) and the original tree (../orig) and makes a
copy of the ones that have changed in the directory ../changed.
$ for i in *; do check all files in the directory
> if [ -f ../orig/$i ]; then it is present in the orig tree
> cmp $i ../orig/$i 2>&1 >/dev/null compare them
> if [ $? -ne 0 ]; then theyre different
> cp -p $i ../changed make a copy
> fi
> fi
> done
* Midnight? That looks like 1 a.m. But remember that UNIX timestamps are all in UTC, and thats 1
a.m. in my time zone. This example really was done with touch.
When copying, we use -p to ensure that the timestamps dont get changed again.
Oops, these patches contain the directory name as well. As the diff header indicates, we can
solve this problem by supplying the -p1 flag to patch. We can also solve the problem by
* When moving directories with tar, it may not seem to be important whether you say tar c . or tar
c *--but it is. If you say *, you will miss out any file names starting with . (period).
moving up one level in the directory hierarchy, since we have stuck to the same directory
names. This message also reminds us that patch is very verbose, so this time we enter:
$ gunzip < /C/incoming/gcc-2.6.1-2.6.2.tar.gz | patch -p1 -s | tee patch.log
1 out of 6 hunks failed--saving rejects to cccp.c.rej
$
What went wrong here? Lets take a look at cccp.c.rej and cccp.c.orig. According to the
hunk, line 3281 should be
if (ip->macro != 0)
In other words, we had already applied this change, probably from a message posted in
gnu.gcc.bugs. Although the patch failed, we dont need to worry: all the patches had been
applied.
Now we have a gcc-2.6.2 source tree in our directory. To upgrade to 2.6.3, we need to apply
the next patch:
$ gunzip < /C/incoming/gcc-2.6.2-2.6.3.diff.gz | patch -p1 -s | tee -a patch.log
We use the -a option to patch here to keep both logspossibly overkill in this case. This
time there are no errors.
After patching, there will be a lot of original files in the directory, along with the one .rej file.
We need to decide when to delete the .orig files: if something goes wrong compiling one of
the patched files, its nice to have the original around to compare. In our case, though, we
have a complete source tree of version 2.6.2 on the same disk, and it contains all the original
files, so we can remove the .orig files:
$ find . -name "*.orig" -print | xargs rm
We use xargs instead of -exec rm {} \; because its faster: -exec rm starts a rm process
for every file, whereas xargs will put as many file names onto the rm command line as possi-
ble. After cleaning up the tree, we back it up. Its taken us a while to create the tree, and if
anything goes wrong, wed like to be able to restore it to its initial condition as soon as possi-
ble.
47
Installation paths
Your system configuration may place constraints on where you can install the software. This
is not normally a problem for individual systems, but on a large, heterogeneous network it
could require more consideration.
Traditionally, non-system software has been installed in the hierarchy /usr/local. This is not
an sthetically pleasing location: the hierarchy can become quite large, and in a network
many systems might share the directory.
One of the best thought-out descriptions of a modern file system structure is in the UNIX Sys-
tem V Application Binary Interface, which is also similar to structures used by SunOS and the
newer BSD variants. In brief, it specifies the following top-level directories:
/ The root directory.
/dev The directory tree containing device files.
/etc Directory for machine-specific configuration files.
/opt Directory for add-on software.
/usr This directory used to be the other file system on a UNIX machine. In the
System V ABI it has lost most of its importance. The ABI states uses only
for /usr/bin and /usr/share, and the name /usr has lost its original meaning:
the ABI specifies /usr only as a location for system files that users may wish
to access.
/usr/bin is intended for Utility programs and commands for the use of all applica-
tions and users. In practice, its better to use this directory for system pro-
grams only.
/usr/share The System V ABI states that /usr/share is intended for architecture-inde-
pendent shareable files. In practice, those versions of System V that still
have man pages put them in /usr/share/man, and terminfo data are stored in
/usr/share/lib/terminfo. The rest of the directory may contain a few other
odds and ends, but these two directories make up over 99% of the content.
The choice of the location /usr/share is not a happy choice: firstly, it is fre-
quently a separate file system, but it must be mounted on a non-root file sys-
tem, and secondly the man pages arent really architecture-independent.
The choice makes more sense from the point of view of the Unix Systems
Group, who are concerned only with pure System V: the man pages are
mainly independent of hardware architecture. However, in a real-world net
you probably have two or three different operating systems, each with their
own man pages.
/var This directory contains files that are frequently modified. Typical subdirec-
tories are /var/tmp for temporary files and /var/spool for printer output, uucp
and news.
The System V ABI does not say anything about where to store user files. The Seventh Edition
typically stored them as a subdirectory of /usr, but newer systems have tended to store them in
a directory called /home.
Preferred tools
Many of the most popular software packages are alternative tools. Free software such as gcc,
emacs and perl have become so popular that they are frequently supplied with proprietary sys-
tem releases, and many other systems have ported them and use them as standard tools. If
you want to use such programs, you need to tell the configuration routines about them.
Depending on the tools you use, you may also need to change the flags that you pass to them.
For example, if you compile with gcc, you may choose to include additional compiler flags
such as -fstrength-reduce, which is specific to gcc.
$ make
gcc -DOS_NAME="FreeBSD" -DProgram=bash -DSYSTEM_NAME="i386" \
-DMAINTAINER="[email protected]" -O -g -DHAVE_SETLINEBUF -DHAVE_VFPRINTF \
-DHAVE_UNISTD_H -DHAVE_STDLIB_H -DHAVE_LIMITS_H -DHAVE_GETGROUPS \
-DHAVE_RESOURCE -DHAVE_SYS_PARAM -DVOID_SIGHANDLER -DOPENDIR_NOT_ROBUST \
-DINT_GROUPS_ARRAY -DHAVE_WAIT_H -DHAVE_GETWD -DHAVE_DUP2 -DHAVE_STRERROR \
-DHAVE_DIRENT -DHAVE_DIRENT_H -DHAVE_STRING_H -DHAVE_VARARGS_H -DHAVE_STRCHR \
-DHAVE_STRCASECMP -DHAVE_DEV_FD -D"i386" -D"FreeBSD" -DSHELL -DHAVE_ALLOCA \
-I. -I. -I././lib/ -c shell.c
The -D arguments pass preprocessor variables that define the configuration information.
An alternative method is to put this information in a file with a name like config.h. Taylor
uucp does it this way: in config.h you will find things like:
/* If your compiler supports prototypes, set HAVE_PROTOTYPES to 1. */
#define HAVE_PROTOTYPES 1
/* The following macros indicate what header files you have. Set the
macro to 1 if you have the corresponding header file, or 0 if you
do not. */
#define HAVE_STDDEF_H 1 /* <stddef.h> */
#define HAVE_STDARG_H 1 /* <stdarg.h> */
#define HAVE_STRING_H 1 /* <string.h> */
I prefer this approach: you have all the configuration information in one place, it is docu-
mented, and its more reliable. Assuming that the Makefile dependencies are correct, any
change to config.h will cause the programs to be recompiled on the next make. As we will see
in Chapter 5, Building the package, page 68, this usually doesnt happen if you modify the
Makefile.
Typically, configuration information is based on the kind of operating system you run and the
kind of hardware you use. For example, if you compile for a Sparc II running SunOS 4.1.3,
you might define sparc to indicate the processor architecture used and sunos4 to indicate the
operating system. Since SunOS 4 is basically UNIX, you might also need to define unix. On
an Intel 486 running UnixWare you might need to define i386 for the processor architecture,*
and SVR4 to indicate the operating system. This information is then used in the source files as
arguments to preprocessor #ifdef commands. For example, the beginning of each source
file, or a general configuration file, might contain:
#ifdef i386
#include "m/i386.h"
#endif
#ifdef sparc
#include "m/sparc.h"
#endif
* Why not i486? The processor is an Intel 486, but the architecture is called the i386 architecture. You
also use i386 when compiling for a Pentium.
#ifdef sunos4
#include "s/sunos4.h"
#endif
#ifdef SVR4
#include "s/usg-4.0.h"
#endif
You can get yourself into real trouble if you define more than one machine architecture or
more than one operating system. Since configuration is usually automated to some extent, the
likelihood of this is not very great, but if you end up with lots of double definitions when
compiling, this is a possible reason.
Configuration through the preprocessor works nicely if the hardware and software both
exactly match the expectations of the person who wrote the code. In many cases, this is not
the case: looking at the example above, note that the file included for SVR4 is s/usg-4.0.h,
which suggests that it is intended for UNIX System V release 4.0. UnixWare is System V
release 4.2. Will this work? Maybe. It could be that the configuration mechanism was last
revised before System V.4.2 came out. If you find a file s/usg-4.2.h, its a good idea to use it
instead, but otherwise its a matter of trial and error.
Most software uses this approach, although it has a number of significant drawbacks:
The choices are not very detailed: for example, most packages dont distinguish between
Intel 386 and Intel 486, although the latter has a floating point coprocessor and the for-
mer doesnt.
There is no general consensus on what abbreviations to use. For UnixWare, you may
find that the correct operating system information is determined by USG (USG is the
Unix Systems Group, which, with some interruption,* is responsible for System V),
SYSV, SVR4, SYSV_4, SYSV_4_2 or even SVR3. This last can happen when the configu-
ration needed to be updated from System V.2 to System V.3, but not again for System
V.4.
The choice of operating system is usually determined by just a couple of differences. For
example, base System V.3 does not have the system call rename, but most versions of
System V.3 that you will find today have it. System V.4 does have rename. A software
writer may use #ifdef SVR4 only to determine whether the system has the rename sys-
tem call or not. If you are porting this package to a version of System V.3.2 with
rename, it might be a better idea to define SVR4, and not SVR3.
Many aspects attributed to the kernel are in fact properties of the system library. As we
will see in the introduction to Part 2 of this book, there is a big difference between kernel
functionality and library functionality. The assumption is that a specific kernel uses the
library with which it is supplied. The situation is changing, though: many companies sell
systems without software development tools, and alternative libraries such as the GNU C
library are becoming available. Making assumptions about the library based on the ker-
nel was never a good ideanow its completely untenable. For example, the GNU C
* The first USG was part of AT&T, and was superseded by UNIX Systems Laboratories (USL). After
the sale of USL to Novell, USL became Novells UNIX Systems Group.
library supplies a function rename where needed, so our previous example would fail
even on a System V.3 kernel without a rename system call if it uses the GNU C library.
As you can imagine, many packages break when compiled with the GNU C library,
through their own fault, not that of the library.
In the example above, it would make a whole lot more sense to define a macro HAS_RENAME
which can be set if the rename function is present. Some packages use this method, and the
GNU project is gradually working towards it, but the majority of packages base their deci-
sions primarily on the combination of machine architecture and operating system.
The results of incorrect configuration can be far-reaching and subtle. In many cases, it looks
as if there is a bug in the package, and instead of reconfiguring, you can find yourself making
significant changes to the source. This can cause it to work for the environment in which it is
compiled, but to break it for anything else.
As the comments suggest, typing make generic should work most of the time. If it doesnt,
looking at the Makefile reveals a whole host of targets for a number of combined hard-
ware/software platforms. If one of them works for you, and you can find which one, then this
might be an easy way to go. If none does, you might find yourself faced with some serious
Makefile rewriting. This method has an additional disadvantage that it might compile with no
problems and run into subtle problems when you try to execute itfor example, if the pro-
gram expects System V sigpause and your system supplies BSD sigpause,* the build
process may complete without detecting any problems, but the program will not run correctly,
and you might have a lot of trouble finding out why.
Manual configuration
Modifying the Makefile or config.h manually is a better approach than multiple Makefile tar-
gets. This seemingly arduous method has a number of advantages:
You get to see what is being changed. If you have problems with the resultant build, its
usually relatively easy to pin-point them.
Assuming that the meanings of the parameters are well documented, it can be easier to
modify them manually than run an automated procedure that hides much of what it is
doing.
If you find you do need to change something, you can usually do it fairly quickly. With
an automated script, you may need to go through the whole script to change a single
minor parameter.
On the down side, manual configuration requires that you understand the issues involved: you
cant do it if you dont understand the build process. In addition, you may need to repeat it
every time you get an update of the package, and it is susceptible to error.
* See Chapter 13, Signals, pages 190 and 192 for further information.
Configuration scripts
Neither multiple Makefile targets nor manual modification of the Makefile leave you with the
warm, fuzzy feeling that everything is going to work correctly. It would be nice to have a
more mechanized method to ensure that the package gets the correct information about the
environment in which it is to be built. One way to do this is to condense the decisions you
need to make in manual configuration into a shell script. Some of these scripts work very
well. A whole family of configuration scripts has grown up in the area of electronic mail and
news. Heres part of the configuration script for C news, which for some reason is called
build:
$ cd conf
$ build
This interactive command will build shell files named doit.root,
doit.bin, doit.news, and again.root to do all the work. It will not
actually do anything itself, so feel free to abort and start again.
C News wants to keep most of its files under a uid which preferably
should be all its own. Its programs, however, can and probably should
be owned by another user, typically the same one who owns most of the
rest of the system. (Note that on a system running NFS, any program
not owned by "root" is a gaping security hole.)
What user id should be used for news files [news]? RETURN pressed
What group id should be used for news files [news]? RETURN pressed
What user id should be used for news programs [bin]? RETURN pressed
What group id should be used for news programs [bin]? RETURN pressed
Do the C News sources belong to bin [yes]? no
You may need to do some of the installation procedures by hand
after the software is built; doit.bin assumes that it has the
power to create files in the source directories and to update
the news programs.
We chose not to use the symbolic links: the script doesnt say why this method is recom-
mended, they dont buy us anything, and symbolic links mean increased access time.
The configuration script continues with many more questions like this. Well pick it up at var-
ious places in the book.
The flexibility of a shell script is an advantage when checking for system features which are
immediately apparent, but most of them require that you go through the whole process from
start to finish if you need to modify anything. This can take up to 10 minutes on each occa-
sion, and they are often interactive, so you cant just go away and let it do its thing.
This method makes life a whole lot easier if the package has already been ported to your par-
ticular platform, and if you are prepared to accept the default assumptions that it makes, but
can be a real pain if not:
You may end up having to modify the configuration scripts, which are not trivial.
Its not always easy to configure things you want. In the example above, we accepted the
default compiler flags. If you want maximum optimization, and the executables should
be installed in /opt/bin instead of the default /usr/local/bin, running configure becomes
significantly more complicated:*
$ CFLAGS="-O3 -g" sh configure --prefix=/opt
The scripts arent perfect. You should really check the resultant Makefiles, and you will
often find that you need to modify them. For example, the configuration scripts of many
packages, including the GNU debugger, gdb, do not allow you to override the preset
value of CFLAGS. In other cases, you can run into a lot of trouble if you do things that
the script didnt expect. I once spent a couple of hours trying to figure out the behaviour
of the GNU make configuration script when porting to Solaris 2.4:
* This example uses the feature of modern shells of specifying environment variables at the beginning of
the command. The program being run is sh, and the definition of CFLAGS is exported only to the pro-
gram being started.
imake
imake is the X11 solution to package configuration. It uses the C preprocessor to convert a
number of configuration files into a Makefile. Here are the standard files for X11R6:
Imake.tmpl is the main configuration file that is passed to the C preprocessor. It is
responsible for including all the other configuration files via the preprocessor #include
directive.
Imake.cf determines the kind of system upon that imake is running. This may be based
on preprocessor variables supplied by default to the preprocessor, or on variables com-
piled in to imake.
site.def describes local preferences. This is one of the few files that you should normally
consider modifying.
As its name implies, <vendor>.cf has a different name for each platform. Imake.tmpl
decides which file to include based on the information returned by Imake.cf. For exam-
ple, on BSD/OS the file bsdi.cf will be included, whereas under SunOS 4 or Solaris 2 the
file sun.cf will be included.
Imake.rules contains preprocessor macros used to define the individual Makefile targets.
Imakefile is part of the package, not the imake configuration, and describes the package
to imake.
You dont normally run imake directly, since it needs a couple of pathname parameters:
instead you have two possibilities:
Run xmkmf, which is a one-line script that supplies the parameters to imake.
Run make Makefile. This assumes that some kind of functinoal Makefile is already
present in the package.
Strangely, make Makefile is the recommended way to create a new Makefile. I dont agree:
one of the most frequent reasons to make a new Makefile is because the old one doesnt work,
or because it just plain isnt there. If your imake configuration is messed up, you can easily
remove all traces of a functional Makefile and have to restore the original version from tape.
xmkmf always works, and anyway, its less effort to type.
Once you have a Makefile, you may not be finished with configuration. If your package con-
tains subdirectories, you may need to create Makefiles in the subdirectories as well. In gen-
eral, the following sequence will build most packages:
$ xmkmf run imake against the Imakefile
$ make Makefiles create subordinate Makefiles
$ make depend run makedepend against all Makefiles
$ make make the packages
$ make install install the packages
Preparation
If youre unlucky, a port can go seriously wrong. The first time that error messages appear
thick and fast and scroll off the screen before you can read them, you could get the impression
that the packages were built this way deliberately to annoy you.
A little bit of preparation can go a long way towards keeping you in control of whats going
on. Here are some suggestions:
59
All of these facts speak in favour of a windowing system such as X11, preferably with a high-
resolution monitor. You can keep your editor (or editors, if they dont easily handle multiple
files) open all the time, and run the compiler and debugger in other windows. If multiple
directories are involved, its easier to maintain multiple xterms, one per directory, than to con-
tinually change directories. A correctly set up xterm will allow you to scroll back as far as
you want I find that 250 lines is adequate.
fi
truncate_target=yes
;;
Keeping notes about problems you have with older versions helps a lot: this example repre-
sents the results of a considerable time spent debugging the make procedure. If you didnt
have the log, youd risk tripping over this problem every time.
Then the fireworks start. You can sit and watch, but it gets rather boring to watch a package
compile for hours on end, so you usually leave it alone once you have a reasonable expecta-
tion that it will not die as soon as you turn your back. The problem is, of course, that you may
come back and find a lot of gobbldegook on the screen, such as:
make[5]: execve: ../../config/makedepend/makedepend: No such file or directory
make[5]: *** [depend] Error 127
make[5]: Leaving directory /cdcopy/SOURCE/X11/X11R6/xc/programs/xsetroot
depending in programs/xstdcmap...
make[5]: Entering directory /cdcopy/SOURCE/X11/X11R6/xc/programs/xstdcmap
checking ../../config/makedepend/makedepend over in ../../config/makedepend first...
make[6]: Entering directory /cdcopy/SOURCE/X11/X11R6/xc/config/makedepend
gcc -DNO_ASM -fstrength-reduce -fpcc-struct-return -fwritable-strings -O \
-I../../config/imake -I../.. OSDefines -DSYSV -DSYSV386 -c include.c
gcc: OSDefines: No such file or directory
In file included from include.c:30:
def.h:133: conflicting types for getline
/opt/include/stdio.h:505: previous declaration of getline
Broken pipe
This is from a real life attempt to compile X11R6, normally a fairly docile port. The target
makedepend failed to compile, but why? The reason has long since scrolled off the screen.*
You can have your cake and eat it too if you use tee to save your output:
$ make 2>&1 | tee -a Make.log
In this case, I specified the -a option, which tells tee to append to any existing Make.log.
If I dont supply this flag, it will erase any previous contents. Depending on what youre
doing, you may or may not want to use this flag.
If youre not sure what your make is going to do, and especially if the Makefile is complicated,
consider using the -n option. This option tells make to perform a dry run: it prints out the
commands that it would execute, but doesnt actually execute them.
These comparatively simple conventions can save a lot of pain. I use a primitive script called
Make which contains just the single line:
make 2>&1 $* | tee -a Make.log
Its a good idea to always use the same name for the log files so that you can find them easily.
Standard targets
Building packages consists of more than just compiling and linking, and by convention many
Makefiles contain a number of targets with specific meanings. In the following sections well
look at some of the most common ones.
make depend
make depend creates a list of dependencies for your source tree, and usually appends it to the
Makefile. Usually it will perform this task with makedepend, but sometimes you will see a
depend target that uses gcc with the -M flag or cpp. depend should be the first target to run,
since it influences which other commands need to be executed. Unfortunately, most Makefiles
dont have a depend target. Its not difficult to write one, and it pays off in the reduction of
strange, unaccountable bugs after a rebuild of the package. Heres a starting point:
depend:
makedepend *.[ch]
This will work most of the time, but to do it correctly you need to analyze the structure of the
package: it might contain files from other languages, or some files might be created by shell
scripts or special configuration programs. Hopefully, if the package is this complicated, it will
also have a depend target.
Even if you have a depend target, it does not always work as well as you would hope. If you
make some really far-reaching changes, and things dont work the way you expect, its worth
starting from scratch with a make clean to be sure that the make still works.
make all
make all is the normal way to perform the build. Frequently, it is the default target (the first
target in the Makefile), and you just need to enter make. This target typically rebuilds the
package but does not install it.
make install
make install installs the compiled package into the local system environment. The usage
varies considerably; well look at this target in more detail in Chapter 9, Installation, page
126.
make clean
make clean normally removes everything that make all has madethe objects, executables
and possibly auxiliary files. You use it after deciding to change a compiler, for example, or to
save space after you have finished an installation. Be careful with make clean: there is no
complete agreement about exactly what it removes, and frequently you will find that it doesnt
remove everything it should, or it is too eager and removes lots of things it shouldnt. make
clean should remove everything that make all can make again the intermediate and instal-
lable files, but not the configuration information that you may have taken days to get right.
make stamp-halfway
Occasionally you see a target like make stamp-halfway. The commands perform a lot of other
things, and at the end just create an empty file called stamp-halfway. This is a short cut to
save lots of complicated dependency checking: the presence of this file is intended to indicate
that the first half of the build is complete, and that a restart of make can proceed directly to the
second half. Good examples of this technique can be found in the Makefile for the GNU C
compiler, and in the X11 source tree, which uses the name DONE for the stamp file.
Things dont always go this smoothly. You may encounter a number of problems:
You may not be able to find a Makefile, or the targets dont work the way you expect.
make may not be able to make any sense of the Makefile.
The Makefile may refer to non-existent files or directories.
make seems to run, but it doesnt rebuild things it should, or it rebuilds things it
shouldnt.
You cant find anything thats wrong, but make still produces obscure error messages.
In the following sections well look at each of these problems. Heres an overview of the
Problem page
Argument list too long 74
"$! nulled, predecessor circle" 71
"Circular dependency dropped" 71
"Commands commence before first target" 70
Comments in command lists 69
"Graph cycles through target 71
Incorrect continuation lines 73
Incorrect dependencies 68
make forgets the current directory 70
"Missing separator - stop" 70
Missing targets 66
No dependency on Makefile 68
No Makefile 64
Nonsensical targets 71
Problems with make clean 72
Problems with subordinate makes 68
Prompts in Makefiles 74
Subordinate makes 72
Syntax errors from the shell 71
Trailing blanks in variables 69
Unable to stop make 71
Wrong flavour of make 66
Wrong Makefile 66
The first thing to check here is whether there is a Makefile. If you dont find Makefile or
makefile, check for one under a different name. If this is the case, the author should have doc-
umented where the Makefile comes fromcheck the README files and other documentation
that came with the package. You may find that the package uses separate Makefiles for differ-
ent architectures. For example, Makefile may be correct only if you are compiling in a BSD
environment. If you want to compile for a System V machine, you may need to specify a dif-
ferent Makefile:
$ make -f Makefile.sysv
This is a pain because its so easy to make a mistake. In extreme cases the compiler will suc-
cessfully create objects, but they will fail to link.
Other possibilities include:
The Makefile is created by the configuration process, and you havent configured yet.
This would be the case if you find an Imakefile (from which you create a Makefile with
xmkmfsee Chapter 4, Package configuration, page 57), or Makefile.in (GNU config-
uresee page 55).
The directory you are looking at doesnt need a Makefile. The Makefile in the parent
directory, also part of the source tree, could contain rules like:
foo/foo: foo/*.c
${CC} foo/*.c -o foo/foo
In other words, the executable is made automatically when you execute make foo/foo in
the parent directory. As a rule, you start building in the root directory of a package, and
perform explicit builds in subdirectories only if something is obviously wrong.
The author of the package doesnt believe in Makefiles, and has provided a shell script
instead. You often see this with programs that originated on platforms that dont have a
make program.
There is really nothing to build the package: the author is used to doing the compilation
manually. In this case, your best bet is to write a Makefile from scratch. The skeleton in
Example 5-1 will get you a surprisingly long way. The empty targets are to remind you
what you need to fill in:
Example 51:
SRCS = list of C source files
OBJS = ${SRCS:.c=.o} corresponding object files
CC=gcc file name of compiler
CFLAGS=-g -O3 flags for compiler
LDFLAGS=-g flags for linker
BINDIR=/opt/bin
LIBDIR=/opt/lib
MANDIR=/opt/man
MAN1DIR=man1
INFODIR=/opt/info
PROGRAM= name of finished program
all: $(PROGRAM)
${CC} ${LDFLAGS} -o ${PROGRAM} ${OBJS}
man:
doc:
install: all
clean:
rm -f \#* * core $(PROGRAM) *.o
Missing targets
Another obvious reason for the error message might be that the target all doesnt exist: some
Makefiles have a different target name for each kind of system to which the Makefile has been
adapted. The README file should tell you if this is the case. One of the more unusual exam-
ples is gnuplot. You need to enter
$ make All
$ make x11 TARGET=Install
The better ones at least warn yousee Chapter 4, Package configuration, page 53, for an
example. I personally dont like these solutions: its so much easier to add the following line
at the top of the Makefile:
BUILD-TARGET = build-bsd
If you then want to build the package for another architecture, you need only change the sin-
gle line defining BUILD-TARGET.
make may not be able to locate a program specified in a command. You get a message
like:
$ make foo.o
/bin/cc -c foo.o -o foo.c
make: execve: /bin/cc: No such file or directory
make: *** [foo.o] Error 127
The compilers and other programs started by make also access files specified in the
source. If they dont find them, youll see a message like
$ make foo.o
gcc -c foo.c -o foo.o
foo.c:1: bar.h: No such file or directory
make: *** [foo.o] Error 1
No matter where the file is missing, the most frequent reasons why it is not found are:
The package has been configured incorrectly. This is particularly likely if you find that
the package is missing a file like config.h.
The search paths are incorrect. This could be because you configured incorrectly, but it
also could be that the configuration programs dont understand your environment. For
example, its quite common to find Makefiles with contents like:
AR = /bin/ar
AS = /bin/as
CC = /bin/cc
LD = /bin/cc
Some older versions of make need this, since they dont look at the PATH environment
variable. Most modern versions of make do look at PATH, so the easiest way to fix such a
Makefile is to remove the directory component of the definitions.
If neither of these methods work, you have the option of searching for the file:
$ find . -name foo.c -print
Incorrect dependencies
One weakness of make is that you have to tell it the interdependencies between the source
files. Unfortunately, the dependency specifications are very frequently incorrect. Even if they
were correct in the source tree as delivered, changing configuration flags frequently causes
other header files to be included, and as a result the dependencies change. Make it a matter of
course to run a make depend after reconfiguring, if this target is suppliedsee page 62 for
details on how to make one.
No dependency on Makefile
What happens if you change the Makefile? If you decide to change a rule, for example, this
could require recompilation of a program. To put it in make terms: all generated files depend
on the Makefile. The Makefile itself is not typically included in the dependency list. It really
should be, but that would mean rebuilding everything every time you change the Makefile, and
in most cases its not needed. On the other hand, if you do change your Makefile in the course
of a port, its a good idea to save your files, do a make clean and start all over again. If every-
thing is OK, it will build correctly without intervention.
The exact Definition starts at the first non-space character after the = and continues to the end
of the line or the start of the comment, if there is one. You can occasionally run into problems
with things like:
MAKE = /opt/bin/make # in case something else is in the path
When starting subsidiary makes, make uses the value of the variable MAKE as the name of the
program to start. In this case it is /opt/bin/make it has trailing blanks, and the exec call
fails. If youre lucky, you get:
$ make
make: dont know how to make make . stop.
This message does give you a clue: there shouldnt be any white space between the name of
the target and the following period. On the other hand, GNU make is friendly and tidies up
trailing blanks, so it says:
$ make
/opt/bin/make subdir note the space before the target name "subdir"
make: execve: /opt/bin/make: No such file or directory
make: *** [suball] Error 127
The only clue you have here is the length of the space on the first line.
Its relatively easy to avoid this sort of problem: avoid comments at the end of definition lines.
The first comment causes make to think that the rule is completed, and it stops. When you fix
this problem by removing the comment, you run into a second one: it doesnt understand the
second comment either. This time it produces an error message. Again, you need to remove
the comment.
So you look for doc.ms in doc, and its there. Whats going on? Each command is run by a
new shell. The first one executes the cd doc and then exits. The second one tries to execute
the groff command. Since the cd command doesnt affect the parent environment, it has no
further effect, and youre still in the original directory. To do this correctly, you need to write
the rule as:
docs:
cd doc; \
${ROFF} ${RFLAGS} doc.ms > doc.ps
This causes make to consider both lines as a single line, which is then passed to a single shell.
The semicolon after the cd is necessary, since the shell sees the command as a single line.
This example is all on one line, but you can break it anywhere if you end each partial line with
a backslash (\). The important thing here is the placement of the semicolons: a rule of thumb
is to put a semicolon where you would otherwise put a newline, but not after then or else.
For more details, check your shell documentation.
In each case, the message is trying to tell you that your dependencies are looping. This partic-
ular example was caused by the dependencies:
docs: man-pages
man-pages: docs
In order to resolve the dependency docs, make first needs to resolve man-pages. But in order
to resolve man-pages, it first needs to resolve docsa real Catch 22 situation. Real-life loops
are, of course, usually more complex.
Nonsensical targets
Sometimes the first target in the Makefile does nothing useful: you need to explicitly enter
make all in order to make the package. There is no good reason for this, and every reason to
fix itsend the mods back to the original author if possible (and be polite).
Subordinate makes
Some subordinate makes use a different target name for the subsidiary makes: you might
write make all, but make might start the subsidiary makes with make subdirs. Although this
cannot always be avoided, it makes it difficult to debug the Makefile. When modifying Make-
files, you may frequently come across a situation where you need to modify the behaviour of
only one subsidiary make. For example, in many versions of System V, the man pages need to
be formatted before installation. Its easy to tell if this applies to your system: if you install
BSD-style unformatted man pages, the man program will just display a lot of hard-to-read
nroff source. Frequently, fixing the Makefile is more work than you expect. A typical Make-
file may contain a target install that looks like:
install:
for dir in ${SUBDIRS}; do \
echo making $@ in $$dir; \
cd $$dir; ${MAKE} ${MDEFINES} $@; \
cd ..; \
done
make $@ expands to make install. One of these subdirectories is the subdirectory doc,
* If this does happen to you, dont despair just yet. Check first whether this is just simple-mindedness
on the part of the Makefilemaybe there is a relatively simple way to recreate the files. If not, and you
forgot to make a backup of your source tree before you started, then you can despair.
which contains the documentation and requires special treatment for the catman pages: they
need to be formatted before installation, whereas the man pages are not formatted until the
first time they are referencedsee Chapter 7, Documentation, page 99 for further informa-
tion. The simplest solution is a different target that singles out the doc and makes a different
target, say install-catman. This is untidy and requires some modifications to the variable
SUBDIRS to exclude doc. A simpler way is to create a new target, install-catman, and modify
all Makefiles to recognize it:
install-catman install-manman:
for dir in ${SUBDIRS}; do \
echo making $@ in $$dir; \
cd $$dir; ${MAKE} ${MDEFINES} $@; \
cd ..; \
done
In the Makefiles in the subdirectories, you might then find targets like
install-catman: ${MANPAGES}
for i in $<; do ${NROFF} -man $$i > ${CATMAN}/$i; done
install-manman: ${MANPAGES}
for i in $<; do cp $$i > ${MANMAN}/$i; done
The rule in the top-level Makefile is the same for both targets: you just need to know the name
to invoke it with. In this example we have also renamed the original install target so that it
doesnt get invoked accidentally. By removing the install target altogether, you need to
make a conscious decision about what kind of man pages that your system wants.
Were not done yet: we now have exactly the situation we were complaining about on page
66: it is still a nuisance to have to remember make install-catman or make install-manman.
We can get round this problem, too, with
INSTALL_TYPE=install-catman
install: ${INSTALL_TYPE}
After this, you can just enter make install, and the target install performs the type of installa-
tion specified in the variable INSTALL_TYPE. This variable needs to be modified from time to
time, but it makes it easier to avoid mistakes while porting.
At some point I decided to change the sequence of chapters, and removed the file tools.ms. I
was not completely sure I wanted to do this, so rather than just changing the Makefile, I com-
mented out the first line and repeated it in the new form:
# PART1 = part1.ms config.ms imake.ms make.ms tools.ms compiler.ms obj.ms \
PART1 = part1.ms config.ms imake.ms make.ms compiler.ms obj.ms \
documentation.ms testing.ms install.ms epilogue.ms
This works just fineat first. In fact, it turns out that make treats all three lines as a com-
ment, since the comment finished with a \ character. As a result, the variable PART1
remained undefined. If you comment out a line that ends in \, you should also remove the \.
Prompts in Makefiles
If you do the Right Thing and copy your make output to a log file, you may find that make just
hangs. The following kind of Makefile can cause this problem:
all: checkclean prog
checkclean:
@echo -n "Make clean first? "
@read reply; if [ "$$reply" = y ]; then make clean; fi
If you copy the output to a file, of course, you dont see the prompt, and it looks as if make is
hanging. This doesnt mean its a bad idea to save your make output: its generally a bad idea
to put prompts into Makefiles. There are some exceptions, of course. The Linux configura-
tion program is a Makefile, and to interactively configure the system you enter make config.
clean:
rm -rf *.o *.a *.depend * core ${INTERMEDIATES}
you can split it into
clean:
rm -rf *.o
rm -rf *.a *.depend * core ${INTERMEDIATES}
In most large trees, the *.o filenames constitute the majority of the arguments, so you
dont need more than two lines.
Even after the previous example, you might find that the length of the *.o parameters is
too long. In this case, you could try naming the objects explicitly:
clean:
rm -rf [a-f]*.o
rm -rf [g-p]*.o
rm -rf [r-z]*.o
rm -rf *.a *.depend * core ${INTERMEDIATES}
clean:
rm -rf ${OBJ1S}
rm -rf ${OBJ2S}
rm -rf ${OBJ3S}
Yet another method involves the use of the xargs program. This has the advantage of not
breaking after new files have been added to the lists:
clean:
find . -name "*.o" -print | xargs rm -f
This chops up the parameter list into chunks that wont overflow the system limits.
there are some other possibilities. You might be able to shorten the pathnames. If you are
building in a directory /next-release/SOURCE/sysv/SCO/gcc-2.6.0, and every file name in
ALLOBJS is absolute, its much easier to exceed the limit than if the directory name was, say,
/S. You could use a symbolic link to solve this problem, but most systems that dont support
ARG_MAX also dont have symbolic links.*
If this doesnt work, you could place the files in a library, possibly using xargs:
${PROG}:
rm libkludge.a
echo ${ALLOBJS} | xargs ar cruv libkludge.a
${CC} libkludge.a -o ${PROG}
This looks strange, since theres no object file, but it works: by the time it finds the name
libkludge.a, the linker has already loaded the object file crt0.o (see Chapter 21, Object files
and friends, page 368), and is looking for a symbol main. It doesnt care whether it finds it in
an object file or a library file.
Modifying Makefiles
Frequently enough, you find that the Makefile is inadequate. Targets are missing, or some
error occurs that is almost untraceable: you need to fix the Makefile. Before you do this, you
should check whether you are changing the correct Makefile. Some packages build a new
Makefile every time you run make. In particular, you frequently see Makefiles that start with
text like
# Makefile generated by imake - do not edit!
You can follow this advice or not: it depends on you and what you are doing: If you are just
trying to figure out what the Makefile is trying (and presumably failing) to do, its nice to
know that you can subsequently delete your modified Makefile and have it automatically
remade.
Once you have found out why the Makefile is doing what it is, you need to fix the source of
the Makefile. This is not usually too difficult: the input files to the Makefile generation phase
typically dont look too different from the finished Makefile. For example, Makefile.in in the
GNU packages is a skeleton that is processed by m4, and except for the m4 parameters Make-
file.in looks very similar to the finished Makefile. Finding the way back to the Imakefile from
the Makefile requires a little more understanding of the imake process, but with a little practice
its not that difficult.
* If you are on a network with other machines with more modern file systems, you could work around
this problem by placing the files on the other system and accessing them via NFS.
Compiler warnings
Its easy to make mistakes when writing programs, but it used to be even easier: nowadays,
even the worst compilers attempt to catch dubious constructs and warn you about them. In
this section, well look at what they can and cant do.
Before compilers worried about coding quality, the program lint performed this task. lint is
still around, but hardly anybody uses it any more, since it doesnt always match the compiler
being used. This is a pity, because lint can catch a number of dubious situations that evade
most compilers.
77
Problems related to program flow. These are detected by the flow analysis pass of the
optimizer. For example:
int a;
b = a;
The second line uses the value of a before it has been assigned a value. The optimizer
notices this omission and may print a warning.
In the following sections, well examine typical warning messages, how they are detected and
how reliable they are. Ill base the sections on the warning messages from the GNU C com-
piler, since the it has a particularly large choice of warning messages, and since it is also
widely used. Other compilers will warn about the same kind of problems, but the messages
may be different. Table 6-1 gives an overview of the warnings well see.
foo (int x)
{
if (x > 3)
return x - 1;
}
If x is greater than 3, this function returns x - 1. Otherwise it returns with some uninitial-
ized value, since there is no explicit return statement for this case. This problem is particu-
larly insidious, since the return value will be the same for every invocation on a particular
architecture (possibly the value of x), but this is a by-product of the way the compiler works,
and may be completely different if you compile it with a different compiler or on some other
architecture.
Uninitialized variables
Consider the following code:
void foo (int x)
{
int a;
if (x > 5)
a = x - 3;
bar (a);
... etc
Depending on the value of x, a may or may not be initialized when you call bar. If you select
the -Wuninitialized compiler option, it warns you when this situation occurs. Some com-
pilers, including current versions of gcc place some limitations on this test.
Since x is unsigned, its value is always >= 0, so the if is superfluous. This kind of problem is
surprisingly common: system header files may differ in opinion as to whether a value is
signed or unsigned. The option -W causes the compiler to issue warnings for this and a whole
lot of other situations.
The intention of xlate is to translate text to a form used by older model HP LaserJet printers.
This code works only if the char *s is unsigned. By default, the C char type is a signed
value, and so the characters 0x80 to 0xff represent a negative array offset, and the program
attempts (maybe successfully) to access a byte outside the table iso_translate. gcc warns
about this if you set the option -Wchar-subscripts.
Dequalifying types
The following code fragment can cause problems:
char *profane;
void foo (const char *holy)
{
profane = holy;
The assignment of holy to profane loses the qualifier const, and the compiler complains
about the fact. On the other hand, this is valid:
profane = (char *) holy;
This doesnt make it a better idea: holy is supposed to be unchangeable, and here you are
removing this qualifier. If you specify the -Wcast-qual option to gcc, it complains if you
use a cast to remove a type qualifier such as const.
In this case, there is a good chance that the int * pointer ip requires a specific alignment and
is not allowed to point at any address in memory the way the char pointer x is allowed to do.
If you specify the -Wcast-align option to gcc, it warns you of such assignments.
This is no longer allowed in ANSI C: indices for switch must evaluate to an int, even if int
and long have the same length. gcc issues a warning about long indices in switch unless
you specify the -traditional option.
Here the storage class specifier static comes after the type specifier int. The ANSI Standard
still permits this, but declares the usage to be obsolescent. gcc issues a warning when it
encounters this and the option -W has been set.
Trigraphs
Trigraphs (see Chapter 20, Compilers, page 342) are no error, at least according to the ANSI
Standard. The Free Software Foundation makes no bones about their opinion of them, and so
gcc supplies the option -Wtrigraphs, which prints a warning if any trigraphs occur in the
source code. Since this works only if the option -trigraphs is used to enable them, it is not
clear that this is of any real use.
Nested comments
Occasionally you see code like
void foo (int x)
{
int y; /* state information
y = bar (); /* initialize y */
if (y == 4)
... etc
The code looks reasonable, and it is syntactically correct C, but in fact the comment after the
declaration of y is not terminated, so it includes the whole of the next line, which is almost
certainly not the intention. gcc recognizes this if it finds the sequence /* in a comment, and
warns of this situation if you specify the -Wcomment option.
Missing parentheses
What value does the following code return?
int a = 11 << 4 & 7 << 2 > 4;
The result is 0, but the real question is: in what order does the compiler evaluate the expres-
sion? You can find the real answer on page 53 of K&R, but you dont want to do that all the
time. We can re-write the code as
int a = (11 << 4) & ((7 << 2) > 4);
This makes it a lot clearer what is intended. gcc warns about what it considers to be missing
parentheses if you select the -Wparentheses option. By its nature, this option is subjective,
and you may find that it complains about things that look fine to you.
The extern declaration was then valid until the end of the source file. In ANSI C, the scope of
open would be the scope of foo: outside of foo, it would no longer be known. gcc issues a
warning about extern statements inside a function definition unless you supply the -tradi-
tional option. If you are using -traditional and want these messages, you can supply
the -Wnested-externs option as well.
Compiler errors
Of course, apart from warnings, you frequently see error messages from the compilerthey
are the most common reason for a build to fail. In this section, well look at some of the more
common ones.
Undefined symbols
This is one of the most frequent compiler error messages you see during porting. At first
sight, it seems strange that the compiler should find undefined symbols in a program that has
already been installed on another platform: if there are such primitive errors in it, how could it
have worked?
In almost every case, you will find one of the following problems:
The definition you need may have been #ifdefed out. For example, in a manually con-
figured package, if you forget to specify a processor architecture, the package may try to
compile with no processor definitions, which is sure to give rise to this kind of problem.
The symbol may have been defined in a header file on the system where it was devel-
oped. This header file is different on your system, and the symbol you need is never
defined.
You may be looking at the wrong header files. Some versions of gcc install fixed
copies of the system header files in their own private directory. For example, under
BSD/386 version 1.1, gcc version 2.6.3 creates a version of unistd.h and hides it in a pri-
vate directory. This file omits a number of definitions supplied in the BSDI version of
unistd.h. You can confirm which header files have been included by running gcc with the
-H option. In addition, on page 86 we look at a way to check exactly what the preproces-
sor did.
The second problem is surprisingly common, even on supposedly identical systems. For
example, in most versions of UNIX System V.4.2, the system header file link.h defines infor-
mation and structures used by debuggers. In UnixWare 1.0, it defines information used by
some Novell-specific communications protocols. If you try to compile gdb under UnixWare
1.0, you will have problems as a result: the system simply does not contain the definitions you
need.
Something similar happens on newer System V systems with POSIX.1 compatibility. A pro-
gram that seems formally correct may fail to compile with an undefined symbol O_NDELAY.
O_NDELAY is a flag to open, which specifies that the call to open should not wait for comple-
tion of the request. This can be very useful, for example, when the open is on a serial line
and will not complete until an incoming call occurs. The flag is supported by almost all mod-
ern UNIX ports, but it is not defined in POSIX.1. The result is that the definition is carefully
removed if you compile defining -D_POSIX_SOURCE.
You might think that this isnt a problem, and that you can replace O_NDELAY with the
POSIX.1 flag O_NONBLOCK. Unfortunately, the semantics of O_NONBLOCK vary from those of
O_NDELAY: if no data is available, O_NONBLOCK returns -1, and O_NDELAY returns 0. You can
make the change, of course, but this requires more modifications to the program, and you have
a strraighforward alternative: #undef _POSIX_SOURCE. If you do this, you may find that
suddenly other macros are undefined, for example O_NOCTTY. System V.4 only defines this
variable if _POSIX_SOURCE is set.
Theres no simple solution to this problem. It is caused by messy programming style: the pro-
grammer has mixed symbols defined only by POSIX.1 with those that are not defined in
POSIX.1. The program may run on your current system, but may stop doing so at the next
release.
Line 156 is, not surprisingly, the definition of puts. But this is a definition, not a call, and
certainly not a macro. And why didnt it complain about all the other definitions? There were
many more than shown here.
In cases like this, its good to understand the way the compiler works well look at this in
more detail in Chapter 20, Compilers, on page 348. At the moment, we just need to recall that
programs are compiled in two stages: first, the preprocessor expands all preprocessor defini-
tions and macros, and then the compiler itself compiles the resultant output, which can look
quite different.
If you encounter this kind of problem, theres a good chance that the compiler is not seeing
what you expect it to see. You can frequently solve this kind of riddle by examining the view
of the source that the compiler sees, the output of the preprocessor. In this section, well look
at the technique I used to solve this particular problem.
All compilers will allow you to run the preprocessor separately from the compiler, usually by
specifying the -E option see your compiler documentation for more details. In this case, I
was running the compiler in an xterm*, so I was able to cut and paste the complete 8-line com-
piler invocation as a command to the shell, and all I needed to type was the text in bold face:
$ gcc -c -DIN_GCC -g -O3 -I. -I. -I./config \
-DGCC_INCLUDE_DIR=\"/opt/lib/gcc-lib/i386--sysv/2.6.0/include\" \
-DGPLUSPLUS_INCLUDE_DIR=\"/opt/lib/g++-include\" \
-DCROSS_INCLUDE_DIR=\"/opt/lib/gcc-lib/i386--sysv/2.6.0/sys-include\" \
-DTOOL_INCLUDE_DIR=\"/opt/i386--sysv/include\" \
-DLOCAL_INCLUDE_DIR=\"/usr/local/include\" \
-DSTD_PROTO_DIR=\"/opt/lib/gcc-lib/i386--sysv/2.6.0\" \
./protoize.c -E -o junk.c
$
If you dont have xterm, you can do the same sort of thing by editing the make log (see Chap-
ter 5, Building the package, page 60), which will contain the invocation as well.
junk.c starts with:
# 1 "./config.h" 1
# 1 "./config/i386/xm-i386.h" 1
40 empty lines
# 1 "./tm.h" 1
19 empty lines
# 1 "./config/i386/gas.h" 1
22 empty lines
This file seems to consist mainly of empty lines, and the lines that arent empty dont seem to
be C! In fact, the # lines are C (see the line directive in Chapter 20, Compilers, page 344),
except that in this case the keyword line has been omitted. The empty lines are where com-
ments and preprocessor directives used to be. The error message referred to line 156 of pro-
toize.c, so I searched for lines with protoize.c on them. I found a number of them:
* xterm is a terminal emulator program that runs under X11. If you dont use X11, you shouldfor
example, it makes this particular technique much easier.
Clearly, the text was between lines 78 and 222. I positioned on the line after the marker for
line 78 and moved down (156 - 78) or 78 lines. There I found:
extern int fflush ();
extern int atoi ();
extern int ((fputs(( ), stdout) || (( stdout )->__bufp < ( stdout )->__put_limit
? (int) (unsigned char) (*( stdout )->__bufp++ = (unsigned char) ( 0 ))
:__flshfp (( stdout ), (unsigned char) ( 0 ))) == (-1) ) ? (-1) : 0) ;
extern int fputs ();
extern int fputc ();
extern int link ();
extern int unlink ();
Well, at any rate this made it clear why the compiler was complaining. But where did this
junk come from? It can be difficult to figure this out. With gcc you can use the -dD option to
keep the preprocessor definitionsunfortunately, the compiler still removes the other pre-
processor directives. I used -dD as well, and found in junk.c:
# 491 "/opt/include/stdio.h" 2
25 lines missing
extern int fputs (__const char *__s, FILE *__stream) ;
/* Write a string, followed by a newline, to stdout. */
extern int puts (__const char *__s) ;
This looks strange: first it declares puts as an external function, then it defines it as a macro.
Looking at the original source of stdio.h, I found:
/* Write a string, followed by a newline, to stdout. */
extern int puts __P ((__const char *__s));
#ifdef __OPTIMIZE__
#define puts(s) ((fputs((s), stdout) || __putc(0, stdout) == EOF) ? EOF : 0)
#endif /* Optimizing. */
No, this doesnt make sense its a real live bug in the header file. At the very least, the dec-
laration of puts () should have been in an #else clause. But thats not the real problem: it
doesnt worry the preprocessor, and the compiler doesnt see it. The real problem is that pro-
toize.c is trying to do the work of the header files and define puts again. There are many pro-
grams that try to out-guess header files: this kind of definition breaks them all.
There are at least two ways to fix this problem, both of them simple. The real question is,
what is the Right Thing? System or library header files should be allowed to define macros
instead of functions if they want, and an application program has no business trying to do the
work of the header files, so it would make sense to fix protoize.c by removing all these exter-
nal definitions: apart from this problem, theyre also incompatible with ANSI C, since they
dont describe the parameters. In fact, I chose to remove the definition from the header file,
since that way I only had to do the work once, and in any case, its not clear that the definition
really would run any faster.
Preprocessor output usually looks even more illegible than this, particularly if lots of clever
nested #defines have been performed. In addition, youll frequently see references to non-
existant line numbers. Here are a couple of ways to make it more legible:
Use an editor to put comments around all the #line directives in the preprocessor out-
put, and then recompile. This will make it easier to find the line in the preprocessor out-
put to which the compiler or debugger is referring; then you can use the comments to fol-
low it back to the original source.
Run the preprocessor output through a program like indent, which improves legibility
considerably. This is especially useful if you find yourself in the unenviable position of
having to modify the generated sources. indent is not guaranteed to maintain the same
number of lines, so after indenting you should recompile.
Other preprocessors
There are many other cases in which the source file you use is not the source file that the com-
piler gets. For example, yacc and bison take a grammar file and make a (more or less illegi-
ble) .c file out of it; other examples are database preprocessors like Informix ESQL, which
takes C source with embedded SQL statements and converts it into a form that the C compiler
can compile. The preprocessors output is intended to be read by a compiler, not by humans.
All of these preprocessors use lines beginning with # to insert information about the original
line numbers and source files into their output. Not all of them do it correctly: if the pre-
processor inserts extra lines into the source, they can become ambiguous, and you can run into
problems when using symbolic debuggers, where you normally specify code locations by line
number.
Syntax errors
Syntax errors in previously functional programs usually have the same causes as undefined
symbols, but they show their faces in a different way. A favourite one results from omitting
/usr/include/sys/types.h. For example, consider bar.c:
#include <stdio.h>
#ifdef USG
#include <sys/types.h>
#endif
ushort num;
int main (int argc, char *argv [])
{
Theres an error because ushort hasnt been defined. The compiler expected a type specifier,
so it reported a syntax error, not an undefined symbol. To fix it, you need to define the type
specified see Appendix A, Comparative reference to UNIX data types for a list of the more
common type specifiers.
If you find yourself with header files that confuse your preprocessor, you can run a differ-
ent preprocessor, collect the output and feed it to your compiler. Since the output of the
preprocessor is not machine-dependent, you could even do this on a different machine
with different architecture, as long as you ensure that you use the correct system header
files. By convention, the preprocessor output for foo.c would be called foo.isee Chap-
ter 20, Compilers, page 348 for a list of intermediate file suffixes though it usually
does no harm if you call it foo.c and pass it through the preprocessor again, since there
should no longer be anything for the second preprocessor to do.
If you want to report a compiler bug, its frequently a good idea to supply the preproces-
sor output: the bug might be dependent on some header file conflict that doesnt exist on
the system where the compiler development takes place.
If you suspect the compiler of generating incorrect code, you can stop compilation after
the compiler pass and collect the generated assembler output.
Preformatted documentation
Occasionally you get documentation that has been formatted so that you can print it on just
about any printer, but this doesnt happen very much: in order to achieve this, the text must be
free of any frills and formatted so that any typewriter can print it. Nearly any printer
* Read The Manualthe F is usually silent.
91
nowadays is capable of better results, so preformatted files are usually supplied in a format
that can print high quality printout on a laser printer. The following three are about the only
ones you will come across:
PostScript is a specialized programming language for printers, and the printed data are in
fact embedded in the program. This makes it an extremely flexible format.
.dvi is the format that is output by TEX. In order to print it, you need a TEX driver.
Unlike PostScript and .dvi, the Hewlett-Packard LaserJet format is not portable: you
need a LaserJet-compatible printer to print it. The LaserJet format is obsolescent: even
many LaserJet printers made today also support PostScript, and there are programmatic
ways to print PostScript on other laser printers, so there is little motivation for using the
much more restrictive LaserJet format.
PostScript
PostScript is the current format of choice. Because it is a programming language, it is much
more flexible than conventional data formats. For example, it is easily scalable. You can take
a file intended for a phototypesetter with a resolution of 2540 bpi and print it on a laser
printer, and it will come out correctly.* In addition, better quality printers perform the format-
ting themselves, resulting in a considerable load reduction for the computer. A large number
of printers and all modern phototypesetters can process PostScript directly.
If your printer doesnt handle PostScript, you can use programs like ghostscript, which inter-
pret PostScript programs and output in a multitude of other formats, including LaserJet, so
even if you have a LaserJet, it can be a better idea to use PostScript format. ghostscript is dis-
tributed by the Free Software Foundation see Appendix E, Where to get sources.
ghostscript can also display PostScript files on X displays.
Most PostScript files are encoded in plain ASCII without any control characters except new-
line (though that doesnt make them easy to read). Even when you include special characters
in your text, they appear in the PostScript document as plain ASCII sequences. Its usually
pretty easy to recognize PostScript, even without the file program. Heres the start of a draft
version of this chapter:
%!PS-Adobe-3.0
%%Creator: groff version 1.09
%%CreationDate: Thu Aug 18 17:34:24 1994
%%DocumentNeededResources: font Times-Bold
The data itself is embedded in parentheses between the commands. Looking at a draft of this
text, we see things like
(It)79.8 273.6 Q 2.613(su)-.55 G .113
(sually pretty easy to recognize a PostScript program, e)-2.613 F -.15
(ve)-.25 G 2.614(nw).15 G .114(ithout the)-2.614 F F2(\214le)2.614 E F1
(program--here)79.8 285.6 Q 2.5(st)-.55 G(he start of a draft v)-2.5 E
* You may have to wait a while before a few megabytes of font information are transferred and pro-
cessed, but eventually you get your document.
This extracts the font requests from the PostScript file: in this case, the document
requires Times Roman, Courier and Garamond fonts. Just about every printer and soft-
ware package supplies Times Roman and Courier, but Garamond (the font in which this
book is written) is less common. In addition, most fonts are copyrighted, so you proba-
bly wont be able to find them on the net. If you have a document like this in PostScript
format, your choices are:
Reformat it with a different font if you have the source.
Get the Garamond fonts.
Edit the file and change the name of the font to a font with similar metrics (in other
words, with similar size characters). The results wont be as good, but if the font
you find is similar enough, they might be acceptable. For example, you might
change the text Garamond to Times Roman.
Wrong font type
Most PostScript fonts are in plain ASCII. You may also come across Type 2 PostScript
and display PostScript, both of which include binary data. Many printers cant under-
stand the binary format, and they may react to it in an unfriendly way. For example, my
National KX-P 4455 printer just hangs if I copy display PostScript to it. See the section
format conversion below for ways to solve this dilemma.
.dvi format
One of the goals of TEX was to be able to create output for just about any printer. As we will
see, old versions of troff, the main competitor, were able to produce output only for a very
limited number of phototypesetters. Even if you have one of them in your office, its unlikely
that you will want to use it for printing out a draft of a 30-page paper.
The TEX solution, which was later adopted by troff in ditroff (device independent troff), was to
output the formatted data in a device-independent format, .dvi, and leave it to another pro-
gram, a so-called driver, to format the files in a format appropriate to the output device.
Unlike PostScript, .dvi contains large numbers of control characters and characters with the
sign bit set, and is not even remotely legible. Most versions of file know about .dvi format.
Format conversion
Not so long ago your choice of documentation software determined your output format. For
example, if you used TEX, you would get .dvi output, and you would need a TEX driver to print
it. Nowadays, its becoming easier to handle file formats. GNU troff will output in .dvi for-
mat if you wish, and programs are available to convert from .dvi to PostScript and back again.
Heres a list of conversions you might like to perform see Appendix E, Where to get sources
for how to get software to perform them.
A number of programs convert from .dvi to PostScriptfor example, dvips.
Theres no good reason to want to convert from PostScript to .dvi, so there are no pro-
grams available. .dvi is not much use in itselfit needs to be tranformed to a final
printer form, and if you have PostScript output, you can do that directly with ghostscript
(see below) without going via .dvi.
To display .dvi files on an X display, use SeeTeX.
To convert from .dvi to a printer output format, use one of the dvi2xxx programs.
To convert from PostScript to a printer format, use ghostscript.
To display PostScript on an X display, you can also use ghostscript, but ghostview gives
you a better interface.
To convert PostScript with binary data into ASCII, use t1ascii.
This assumes the use of groff or another flavour of troff that creates PostScript output (thus the
name rcs.ps for the output file). If you do this, you get an output that looks like:
Besides the operations ci and co, RCS provides the following commands: tab(%); li l.
ident%extract identification markers rcs%change RCS file attributes rcsclean%remove
unchanged working files (optional) rcsdiff%compare revisions rcsfreeze%record a configura-
tion (optional) rcsmerge%merge revisions rlog%read log messages and other information in
RCS files A synopsis of these commands appears in the Appendix. Automatic Identification
RCS can stamp source and object code with special identification strings, similar to product
and serial numbers. To obtain such identification, place the marker Id into the text of a revi-
sion, for instance inside a comment. The check-out operation will replace this marker with a
string of the form Id: filename revisionnumber date time author state locker
Most of the text seems to be there, but it hasnt been formatted at all (well, it has been right
justified). What happened?
Almost every troff or roff input document uses some set of macros. You can define your own
macros in the source, of course, but over time a number of standard macro packages have
evolved. They are stored in a directory called tmac. In the days of no confusion, this was
/usr/lib/tmac, but nowadays it might equally well be /usr/share/tmac (for systems close to the
System V.4 ABIsee Chapter 4, Package configuration, page 48, for more details) or
/usr/local/groff/tmac for GNU roff. The name is known to troff either by environment vari-
ables or by instinct (the path name is compiled into the program). troff loads specific macros
if you specify the name of the file as an argument to the -m flag. For example, to specify the
man page macros /usr/lib/tmac/an, you would supply troff with the parameter -man. man
makes more sense than an, so these macros are called the man macros. The names of other
macro packages also usually grow an m at the beginning. Some systems change the base
name of the macros from, say, /usr/lib/tmac/an to /usr/lib/tmac/tmac.an.
Most versions of troff supply the following macro packages:
The man (tmac/an) and mandoc (tmac/andoc) packages are used to format man pages.
The mdoc (tmac/doc) package is used to format hardcopy documents, including some
man pages.
The mm (tmac/m) macros, the so-called memorandum macros, are described in the docu-
mentation as macros to format letters, reports, memoranda, papers, manuals and books.
It doesnt describe what you shouldnt use them for.
The ms (tmac/s) macros were the original macros supplied with the Seventh Edition.
They are now claimed to be obsolescent, but you will see them again and again. This
book was formatted with a modified version of the ms macros.
The me (tmac/e) macros are another, more recent set of macros which originated in
Berkeley.
There is no sure-fire way to tell which macros a file needs. Here are a couple of possibilities:
The file name suffix might give a hint. For example, our file is called rcs.ms, so there is a
very good chance that it wants to be formatted with -ms.
The program grog, which is part of groff, examines the source and guesses the kind of
macro set. It is frequently wrong.
The only other way is trial and error. There arent that many different macro sets, so this
might be a good solution.
In this case, our file name suggests that it should be formatted with the ms macros. Lets try
that:
$ troff rcs.ms >rcs.ps
Now we get:
Besides the operations ci and co, RCS provides the following commands:
tab(%); li l. ident%extract identification markers rcs%change RCS file attributes
rcsclean%remove unchanged working files (optional) rcsdiff%compare revisions rcs-
freeze%record a configuration (optional) rcsmerge%merge revisions rlog%read log messages
and other information in RCS files A synopsis of these commands appears in the Appendix.
2.1 Automatic Identification
RCS can stamp source and object code with special identification strings, similar to product
and serial numbers. To obtain such identification, place the marker
$Id$
into the text of a revision, for instance inside a comment. The check-out operation will replace
this marker with a string of the form
$Id: filename revisionnumber date time author state locker $
Well, it doesnt look quite as bad, but its still not where we want to be. What happened to
that list of program names?
troff does not do all the work by itself. The tabular layout of the program names in this exam-
ple is done by the preprocessor tbl, which handles tables. Before we let troff at the document,
we need to pass it through tbl, which replaces the code
.TS
tab(%);
li l.
ident%extract identification markers
rcs%change RCS file attributes
rcsclean%remove unchanged working files (optional)
rcsdiff%compare revisions
rcsfreeze%record a configuration (optional)
rcsmerge%merge revisions
rlog%read log messages and other information in RCS files
.TE
with a couple of hundred lines of complicated and illegible troff instructions to build the table.
To get the desired results, we need to enter:
$ tbl rcs.ms | troff -ms >rcs.ps
nroff, troff and groff use a number of preprocessors to perform special functions. They are:
soelim replaces .so statements (which correspond to C #include statements) with the con-
tents of the file to which the line refers. The roff programs do this too, of course, but the
other preprocessors dont, so if the contents of one of the files is of interest to another
preprocessor, you need to run soelim first.
refer processes references.
pic draws simple pictures.
tbl formats data in tabular form.
eqn formats equations.
Unless you know that the document youre formatting doesnt use any of these preprocessors,
or formatting takes a very long time, its easier to use them all. There are two possible ways
to do this:
You can pipe from one processor to the next. This is the standard way:
$ soelim rcs.ms | refer | pic | tbl | eqn | troff -ms
The soelim preprocessor reads in the document, and replaces any .so commands by the
contents of the file to which they refer. It then passes the output to refer, which pro-
cesses any textual references and passes it to pic, which processes any pictures it may
find, and passes the result to tbl. tbl processes any tables and passes its result to eqn,
which processes any equations before passing the result to troff.
Some versions of troff invoke the preprocessors themselves if passed appropriate flags.
For example, with groff:
Flag Processor
-e eqn
-t tbl
-p pic
-s soelim
-R refer
Starting the preprocessors from troff not only has the advantage of involving less typingit
also ensures that the preprocessors are started in the correct sequence. Problems can arise if
you run eqn before tbl, for example, when there are equations within tables. See Typesetting
tables with tbl by Henry McGilton and Mary McNabb for further details.
optionally be installed with a name beginning in gfor example, GNU eqn may be installed
as geqn if the system already has a different version of eqn. indxbib and lookbib (sometimes
called lkbib) process bibliographic references, and are available in the groff package if you
dont have them. groff also includes a number of other programs, such as grops, and grotty,
which you dont normally need to invoke directly.
Man pages
Almost from the beginning, UNIX had an on-line manual, traditionally called man pages.
You can peruse man pages with the man program, or you can print them out as hardcopy doc-
umentation.
Traditionally, man pages are cryptic and formalized: they were introduced at a time when disk
storage was expensive, so they are short, and they were intended as a reference for people who
already understand the product. More and more, unfortunately, they are taking on the respon-
sibility of being the sole source of documentation. They dont perform this task very well.
man history
The UNIX man facility has had a long and varying history, and knowing it helps understand
some of the strangenesses. The Seventh Edition of the Unix Programmers Manual was
divided into nine sections. Section 9, which contained the quick reference cards, has since
atrophied. Traditionally, you refer to man pages by the name of the item to which they refer,
followed by the section number in parentheses, so the man page for the C compiler would be
called cc(1). BSD systems have substantially retained the Seventh Edition structure, but Sys-
tem V has reorganized them. There are also differences of opinion about where individual
man pages belong, so Table 7-2 can only be a guide:
What distinguished the UNIX manual from that of other systems was that it was designed to
be kept online. Each of these sections, except for the quick reference cards, was stored in
nroff format in a directory called /usr/man/man<section>, where <section> was the section
number. Each entry was (and is) called a man page, although nowadays some can run on for
100 pages or more.
The manual was stored in nroff format in order to be independent of the display hardware, and
because formatting the whole manual took such a long time. For these reasons it was chosen
to format pages on an individual basis when they were accessed, which made access to the
manual slower and thus less attractive to use.
The speed problem was solved by saving the formatted copy of the man page in a second
directory hierarchy, /usr/man/cat<section>, the first time that the page was formatted. Subse-
quent accesses would then find the formatted page and display that more quickly.
This basic hierarchy has survived more or less intact to the present day. People have, of
course, thought of ways to confuse it:
As the manual got larger, it seemed reasonable to subdivide it further. Most users
werent interested in system administration functions, so some systems put them into a
separate directory, such as /usr/man/cat1m, or gave them a filename suffix such as m, so
that the manual page for shutdown might end up being called /usr/man/cat1/shut-
down.1m or /usr/man/man1m/shutdown.1m or something similar.
Various commercial implementations reorganized the sequence of the sections in the
printed manual, and reorganized the directories to coincide. For example, in System V
the description of the file /etc/group is in section 4, but in the Seventh Edition and BSD it
is in section 5.
Even without the uncertainty of which section to search for a command, it was evident
that section numbers were not very informative. Some implementations, such as XENIX
and some versions of System V, chose to replace the uninformative numbers with unin-
formative letters. For example, ls(1) becomes ls(C) in XENIX.
Some man programs have lost the ability to format the man pages, so you need to format
them before installation. Youll find this problem on systems where nroff is an add-on
component.
There is no longer a single directory where you can expect to put man pages: some Sys-
tem V versions put formatted man pages for users in a directory /usr/catman/u_man, and
man pages for programmers in /usr/catman/p_man. Since most programmers are users,
and the distinction between the use of the man pages is not always as clear as you would
like, this means that man has to search two separate directory hierarchies for the man
pages.
As we saw in Chapter 4, Package configuration, page 48, System V.4 puts its man pages
in /usr/share/man. Many System V.4 systems require formatted man pages, and some,
such as UnixWare, dont provide a man program at all.
Many man programs accept compressed input, either formatted or non-formatted. For
some reason, the pack program still survives here, but other versions of man also under-
stand man pages compressed with compress or gzip. We looked at all of these programs
Most packages assume that unformatted man pages will be installed in /usr/man. They usu-
ally accept that the path may be different, and some allow you to change the subdirectory and
the file name suffix, but this is as far as they normally go.
This lack of standardization can cause such problems that many people just give up and dont
bother to install the man pages. This is a pityinstead, why not install a man program that
isnt as fussy? A number of alternatives are available, including one for System V.4 from
Walnut Creek and a number on various Linux distributions.
TeX
TEX is Donald Knuths monument to the triumph of logic over convention. To quote Knuths
The TEX book,
Insiders pronounce the of TEX as a Greek chi, not as an x, so that TEX rhymes with the word
blecchhh. Its the ch sound in Scottish words like loch or German words like ach; its a
Spanish j and a Russian kh. When you say it correctly to your computer, the terminal may
become slightly moist.
This is one of the more informative parts of The TEX book. It is, unfortunately, not a manual
but a textbook, and most of the essential parts are hidden in exercises flagged very difficult.
If you just want to figure out how to format a TEX document, Making TEX work, by Norman
Walsh, is a much better option.
If troff input looks like a fly having left an inkwell, TEX input resembles more the attempts of a
drunken spider. Heres part of the file plain.tex which defines some of the things that any TEX
macro package should be able to do:
\def\cases#1{\left\{\,\vcenter{\normalbaselines\m@th
\ialign{$##\hfil$&\quad##\hfil\crcr#1\crcr}}\right.}
\def\matrix#1{\null\,\vcenter{\normalbaselines\m@th
\ialign{\hfil$##$\hfil&&\quad\hfil$##$\hfil\crcr
\mathstrut\crcr\noalign{\kern-\baselineskip}
#1\crcr\mathstrut\crcr\noalign{\kern-\baselineskip}}}\,}
More than anywhere else in porting, it is good for your state of mind to steer clear of TEX
internals. The assumptions on which the syntax is based differ markedly from those of other
programming languages. For example, identifiers may not contain digits, and spaces are
required only when the meaning would otherwise be ambiguous (to TEX, not to you), so the
sequence fontsize300 is in fact the identifier fontsize followed by the number 300. On
the other hand, it is almost impossible to find any good solid information in the documenta-
tion, so you could spend hours trying to solve a minor problem. I have been using TEX fre-
quently for years, and I still find it the most frustrating program I have ever seen.*
Along with TEX, there are a couple of macro packages that have become so important that they
are almost text processors in their own right:
LATEX is a macro package that is not quite as painful as plain TEX, but also not as power-
ful. It is normally built as a separate program when installing TEX, using a technique of
dumping a running program to an object file that we will examine in Chapter 21, Object
files and friends, page 376.
BIBTEX is an auxiliary program which, in conjuntion with LATEX, creates bibliographic
references. Read all about it in Making TEX work. It usually takes three runs through the
source files to create the correct auxiliary files and format the document correctly.
texinfo is a GNU package that supplies both online and hardcopy documentation. It uses
TEX to format the hardcopy documentation. Well look at it along with GNU info in the
next section.
* When I wrote this sentence, I wondered if I wasnt overstating the case. Mike Loukides, the author of
Programming with GNU Software, reviewed the final draft and added a single word: Amen.
Well, maybe youre not quite done after all. Occasionally the program does not work as
advertised. What you do now depends on how much programming experience you have. If
you are a complete beginner, you could be in troubleabout the only thing you can do (apart
from asking somebody else) is to go back and check that you really did configure the package
correctly.
On the other hand, if you have even a slight understanding of programming, you should try to
analyze the cause of the errorits easier than you think. Hold on, and try not to look down.
There are thousands of possible reasons for the problems you encounter when you try to run a
buggy executable, and lots of good books explain debugging techniques. In this chapter, we
will touch only on aspects of debugging that relate to porting. First well attack a typical, if
somewhat involved, real-life bug, and solve it, discussing the pros and cons on the way. Then
well look at alternatives to traditional debuggers: kernel and network tracing.
Before you even start your program, of course, you should check if any test programs are
available. Some packages include their own tests, and separate test suites are available for
others. For other packages there may be test suites that were not designed for the package,
but that can be used with it. If there are any tests, you should obviously run them. You might
also consider writing some tests and including them as a target test in the Makefile.
105
A latent bug has found more fertile feeding ground. For example, a program may read
from a null pointer. This frequently doesnt get noticed if the data at address 0 doesnt
cause the program to do anything unusual. On the other hand, if the new platform does
not have any memory mapped at address 0, it will cause a segmentation violation or a
bus error.
Differences in the implementation of library functions or kernel functionality cause the
program to behave differently in the new environment. For example, the function setp-
grp has completely different semantics under System V and under BSD. See Chapter
12, Kernel dependencies, page 171, for more details.
The configuration scripts have never been adequately tested for your platform. As a
result, the program contains bugs that were not in the original versions.
If you have a communications line trace program, you can try to divide your program
into pieces that communicate across this line, so you can see what they are saying to each
other.
Of course, we have all these things. In the following sections well look at each of them in
more detail.
Symbolic debuggers
If you dont have a symbolic debugger, get one. Now. Many people still claim to be able to
get by without a debugger, and its horrifying how many people dont even know how to use
one. Of course you can debug just about anything without a symbolic debugger. Historians
tell us that you can build pyramids without wheelsthats a comparable level of technology
to testing without a debugger. The GNU debugger, gdb, is available on just about every plat-
form youre likely to encounter, and though its not perfect, it runs rings around techniques
like putting printf statements in your programs.
In UNIX, a debugger is a process that takes control of the execution of another process. Most
versions of UNIX allow only one way for the debugger to take control: it must start the
process that it debugs. Some versions, notably SunOS 4, but not Solaris 2, also allow the
debugger to attach to a running process.
Whichever debugger you use, there are a surprisingly small number of commands that you
need. In the following discussion, well look at the command set of gdb, since it is widely
used. The commands for other symbolic debuggers vary considerably, but they normally have
similar purposes.
A stack trace command answers the question, Where am I, and how did I get here?,
and is almost the most useful of all commands. Its certainly the first thing you should
do when examining a core dump or after getting a signal while debugging the program.
gdb implements this function with the backtrace command.
Displaying data is the most obvious requirement: what is the current value of the vari-
able bar? In gdb, you do this with the print command.
Displaying register contents is really the same thing as displaying program data. In gdb,
you display individual registers with the print command, or all registers with the info
registers command.
Modifying data and register contents is an obvious way of modifying program execution.
In gdb, you do this with the set command.
breakpoints stop execution of the process when the process attempts to execute an
instruction at a certain address. gdb sets breakpoints with the break command.
Many modern machines have hardware support for more sophisticated breakpoint mech-
anisms. For example, the i386 architecture can support four hardware breakpoints on
instruction fetch (in other words, traditional breakpoints), memory read or memory write.
These features are invaluable in systems that support them; unfortunately, UNIX usually
does not. gdb simulates this kind of breakpoint with a so-called watchpoint. When
watchpoints are set, gdb simulates program execution by single-stepping through the pro-
gram. When the condition (for example, writing to the global variable foo) is fulfilled,
the debugger stops the program. This slows down the execution speed by several orders
of magnitude, whereas a real hardware breakpoint has no impact on the execution speed.*
Jumping (changing the address from which the next instruction will be read) is really a
special case of modifying register contents, in this case the program counter (the register
that contains the address of the next instruction). This register is also sometimes called
the instruction pointer, which makes more sense. In gdb, use the jump command to do
this. Use this instruction with care: if the compiler expects the stack to look different at
the source and at the destination, this can easily cause incorrect execution.
Single stepping in its original form is supported in hardware by many architectures: after
executing a single instruction, the machine automatically generates a hardware interrupt
that ultimately causes a SIGTRAP signal to the debugger. gdb performs this function with
the stepi command.
You wont want to execute individual machine instructions until you are in deep trouble.
Instead, you will execute a single line instruction, which effectively single steps until you
leave the current line of source code. To add to the confusion, this is also frequently
called single stepping. This command comes in two flavours, depending on how it treats
function calls. One form will execute the function and stop the program at the next line
after the call. The other, more thorough form will stop execution at the first executable
line of the function. Its important to notice the difference between these two functions:
both are extremely useful, but for different things. gdb performs single line execution
omitting calls with the next command, and includes calls with the step command.
There are two possible approaches when using a debugger. The easier one is to wait until
something goes wrong, then find out where it happened. This is appropriate when the process
gets a signal and does not overwrite the stack: the backtrace command will show you how it
got there.
Sometimes this method doesnt work well: the process may end up in no-mans-land, and you
see something like:
Program received signal SIGSEGV, Segmentation fault.
0x0 in ?? ()
(gdb) bt abbreviation for backtrace
#0 0x0 in ?? () nowhere
(gdb)
Before dying, the process has mutilated itself beyond recognition. Clearly, the first approach
wont work here. In this case, we can start by conceptually dividing the program into a num-
ber of parts: initially we take the function main and the set of functions which main calls. By
single stepping over the function calls until something blows up, we can localize the function
in which the problem occurs. Then we can restart the program and single step through this
* Some architectures slow the overall execution speed slightly in order to test the hardware registers.
This effect is negligible.
function until we find what it calls before dying. This iterative approach sounds slow and tir-
ing, but in fact it works surprisingly well.
The stack trace shows that the main program called XtAppInitialize, and the rest of the
stack shows the program deep in the X Toolkit, one of the central X11 libraries. If this were a
program that you had just written, you could expect it to be a bug in your program. In this
case, where we have just built the complete X11 core system, theres also every possibility
that it is a library bug. As usual, the library was compiled without debug information, and
without that you hardly have a hope of finding it.
Apart from size constraints, there is no reason why you cant include debugging information
in a library. The object files in libraries are just the same as any others we discuss them in
detail on page 369. If you want, you can build libraries with debugging information, or you
can take individual library routines and compile them separately.
Unfortunately, the size constraints are significant: without debugging information, the file
libXt.a is about 330 kB long and contains 53 object files. With debugging information, it
might easily reach 20 MB, since all the myriad X11 global symbols would be included with
each object file in the archive. Its not just a question of disk space: you also need virtual
memory during the link phase to accommodate all these symbols. Most of these files dont
interest us anyway: the first one that does is the one that contains _XtMemmove. So we find
where it is and compile it alone with debugging information.
Thats not as simple as it sounds: first we need to find the source file, and to do that we need
to find the source directory. We could read the documentation, but to do that we need to know
that the Xt functions are in fact the X toolkit. If were using GNU make, or if our Makefile
documents directory changes, an alternative would be to go back to our make log and look for
the text Xt. If we do this, we quickly find
make[4]: Leaving directory /X/X11R6/xc/lib/Xext
making Makefiles in lib/Xt...
mv Makefile Makefile.bak
make[4]: Entering directory /X/X11R6/xc/lib/Xt
make[4]: Nothing to be done for Makefiles.
make[4]: Leaving directory /X/X11R6/xc/lib/Xt
So the directory is /X/X11R6/xc/lib/Xt. The next step is to find the file that contains XtMem-
move. There is a possibility that it is called XtMemmove.c, but in this case there is no such
file. Well have to grep for it. Some versions of grep have an option to descend recursively
into subdirectories, which can be very useful if you have one available. Another useful tool is
cscope, which is supplied with System V.
$ grep XtMemmove *.c
Alloc.c:void _XtMemmove(dst, src, length)
Convert.c: XtMemmove(&p->from.addr, from->addr, from->size);
... many more references to XtMemmove
So XtMemmove is in Alloc.c. By the same method, we look for the other functions mentioned
in the stack trace and discover that we also need to recompile Initialize.c and Display.c.
In order to compile debugging information, we add the compiler option -g. At the same time,
we remove -O. gcc doesnt require this, but its usually easier to debug a non-optimized pro-
gram. We have three choices of how to set the options:
We can modify the Makefile (make World, the main make target for X11, rebuilds the
Makefiles from the corresponding Imakefiles, so this is not overly dangerous).
If we have a working version of xterm, we can use its facilities: first we start the compila-
tion with make, but we dont need to wait for the compilation to complete: as soon as the
compiler invocation appears on the screen, we abort the build with CTRL-C. Using the
xterm copy function, we copy the compiler invocation to the command line and add the
options we want:
$ rm Alloc.o Initialize.o Display.o remove the old objects
$ make and start make normally
rm -f Alloc.o
gcc -DNO_ASM -fstrength-reduce -fpcc-struct-return -c -I../.. \
-DNO_AF_UNIX -DSYSV -DSYSV386 -DUSE_POLL Alloc.c
C interrupt make with CTRL-C
make: *** [Alloc.o] Interrupt
copy the invocation lines above with the mouse, and paste below, then
modify as shown in bold print
$ gcc -DNO_ASM -fstrength-reduce -fpcc-struct-return -c -I../.. \
-DNO_AF_UNIX -DSYSV -DSYSV386 -DUSE_POLL Alloc.c -g
You can also use make -n, which just shows the commands that make would execute,
rather than aborting the make, but you frequently find that make -n prints out a whole
lot of stuff you dont expect. When you have made Alloc.o, you can repeat the process
When we have created all three new object files, we can let make complete the library for us.
It will not try to remake these object files, since now they are newer than any of their depen-
dencies:
$ make run make to build a new library
rm -f libXt.a
ar clq libXt.a ActionHook.o Alloc.o ArgList.o Callback.o ClickTime.o Composite.o \
Constraint.o Convert.o Converters.o Core.o Create.o Destroy.o Display.o Error.o \
Event.o EventUtil.o Functions.o GCManager.o Geometry.o GetActKey.o GetResList.o \
GetValues.o HookObj.o Hooks.o Initialize.o Intrinsic.o Keyboard.o Manage.o \
NextEvent.o Object.o PassivGrab.o Pointer.o Popup.o PopupCB.o RectObj.o \
Resources.o Selection.o SetSens.o SetValues.o SetWMCW.o Shell.o StringDefs.o \
Threads.o TMaction.o TMgrab.o TMkey.o TMparse.o TMprint.o TMstate.o VarCreate.o \
VarGet.o Varargs.o Vendor.o
ranlib libXt.a
rm -f ../../usrlib/libXt.a
cd ../../usrlib; ln ../lib/Xt/libXt.a .
$
Now we have a copy of the X Toolkit in which these three files have been compiled with sym-
bols. Next, we need to rebuild xterm. Thats straightforward enough:
$ cd ../../programs/xterm/
$ pwd
/X/X11R6/xc/programs/xterm
$ make
rm -f xterm
gcc -DNO_ASM -fstrength-reduce -fpcc-struct-return -fwritable-strings -o xterm \
-L../../usrlib main.o input.o charproc.o cursor.o util.o tabs.o screen.o \
scrollbar.o button.o Tekproc.o misc.o VTPrsTbl.o TekPrsTbl.o data.o menu.o -lXaw \
-lXmu -lXt -lSM -lICE -lXext -lX11 -L/usr/X11R6/lib -lpt -ltermlib
Finally, we try again. Since the library is not in the current directory, we use the dir com-
mand to tell gdb where to find the sources. Now we get:
$ gdb xterm
(gdb) dir ../../lib/X11 set source paths
Source directories searched:
/X/X11/X11R6/xc/programs/xterm/../../lib/X11:$cdir:$cwd
(gdb) dir ../../lib/Xt
This shows a typical byte for byte memory move. About the only thing that could cause a bus
error on that statement would be an invalid address, but the parameters show that they appear
to be valid.
There are at two possible gotchas here:
The debugger may be lying. The parameters it shows are the parameters on the stack. If
the code has been optimized, there is a very good chance that the source and destination
addresses are stored in registers, and thus the value of dst on the stack is not up to date.
The destination address may be in the text segment, in which case an attempt to write to
it will cause some kind of error. Depending on the system it could be a segmentation
violation or a bus error.
The most reliable way to find out what is really going on is to look at the machine instructions
being executed. First we tell the debugger to look at current instruction and the following five
instructions:
(gdb) x/6i $eip list the next 6 instructions
0x3ced6 <_XtMemmove+74>: movb %al,(%edx)
0x3ced8 <_XtMemmove+76>: incl 0xc(%ebp)
0x3cedb <_XtMemmove+79>: incl 0x8(%ebp)
0x3cede <_XtMemmove+82>: jmp 0x3cec2 <_XtMemmove+54>
0x3cee0 <_XtMemmove+84>: leave
0x3cee1 <_XtMemmove+85>: ret
The first instruction is a byte move, from register al to the address stored in register edx.
Lets look at the address in edx:
(gdb) p/x $edx
$9 = 0x342d8
Well, this is our dst address alrightwhy cant it store there? It would be nice to be able to
try to set values in memory and see if the debugger can do it:
(gdb) set *dst = Xb
(gdb) p *dst
$13 = 88 X
That looks writable enough. Unfortunately, you cant rely on the debugger to tell the truth.
Debuggers must be able to write to the text segment. If the write had failed, you could have
been sure that the address was not writable, but if the write succeeds, you cant be sure. What
we need to know are the exact segment limits. Some debuggers show you the segment limits,
but current versions of gdb do not. An alternative is the size command:
$ size xterm
text data bss dec hex filename
846204 56680 23844 926728 e2408 xterm
The text segment is 846204 decimal bytes long (0xce97c), and on this system (SCO UNIX) it
starts at address 0, so the address is, indeed, in the text segment. But where did it come from?
To find an answer to that question, we need to look at the calling function. In gdb, we do this
with the frame command:
(gdb) f 1 look at the calling function (frame 1)
#1 0x35129 in _MergeOptionTables (src1=0x342d8, num_src1=24,
src2=0x400ffe, num_src2=64, dst=0x7ffff9c0, num_dst=0x7ffff9bc)
at Initialize.c:602
602 (void) memmove(table, src1, sizeof(XrmOptionDescRec) * num_src1 );
Thats funnylast time it died, the function was called from XtScreenDatabase,* not from
_MergeOptionTables. Why? At the moment its difficult to say for sure, but its possible
that this difference happened because we removed optimization. In any case, we still have a
problem, so we should fix this one first and then go back and look for the other one if solving
this problem isnt enough.
In this case, the frame command doesnt help much, but it does tell us that the destination
variable is called table, and implicitly that memmove has been defined as _XtMemmove in this
source file. We could now look at the source file in an editor in a different X window, but its
easier to list the instructions around the current line with the list command:
(gdb) l
597 enum {Check, NotSorted, IsSorted} sort_order = Check;
598
599 *dst = table = (XrmOptionDescRec*)
600 XtMalloc( sizeof(XrmOptionDescRec) * (num_src1 + num_src2) );
601
602 (void) memmove(table, src1, sizeof(XrmOptionDescRec) * num_src1 );
603 if (num_src2 == 0) {
604 *num_dst = num_src1;
605 return;
606 }
So, the address is returned by the function XtMallocit seems to be allocating storage in the
text segment. At this point, we could examine it more carefully, but lets first be sure that
were looking at the right problem. The address in table should be the same as the address
in the parameter dst of XtMemmove. Were currently examining the environment of _Mer-
geOptionTables, so we can look at it directly:
(gdb) p table
$29 = (XrmOptionDescRec *) 0x41c800
That looks just fine. Where did this strange dst address come from? Lets set a breakpoint
* See frame 1 in the stack trace on page 109.
on the call to memmove on line 602, and then restart the program:
Example 81:
(gdb) b 602
Breakpoint 8 at 0x35111: file Initialize.c, line 602.
(gdb) r
The program being debugged has been started already.
Start it from the beginning? (y or n) y
Starting program: /X/X11/X11R6/xc/programs/xterm/xterm
This is really strange! table has a valid address in the data segment, but the address we pass
to _XtMemmove is in the text segment and seems unrelated. Its not clear what we should look
at next:
The source of the function calls memmove, but after preprocessing it ends up calling
_XtMemmove. memmove might simply be defined as _XtMemmove, but it might also be
defined with parameters, in which case some subtle type conversions might result in our
problem.
If you understand the assembler of the system, it might be instructive to look at the actual
instructions that the compiler produces.
Its definitely quicker to look at the assembler instructions than to fight your way through the
thick undergrowth in the X11 source tree:
(gdb) x/8i $eip look at the next 8 instructions
0x35111 <_MergeOptionTables+63>: movl 0xc(%ebp),%edx
0x35114 <_MergeOptionTables+66>: movl %edx,0xffffffd8(%ebp)
0x35117 <_MergeOptionTables+69>: movl 0xffffffd8(%ebp),%edx
0x3511a <_MergeOptionTables+72>: shll $0x4,%edx
0x3511d <_MergeOptionTables+75>: pushl %edx
0x3511e <_MergeOptionTables+76>: pushl 0xfffffffc(%ebp)
0x35121 <_MergeOptionTables+79>: pushl 0x8(%ebp)
0x35124 <_MergeOptionTables+82>: call 0x3ce8c <_XtMemmove>
This isnt easy stuff to handle, but its worth understanding, so well pull it apart, instruction
for instruction. Its easier to understand this discussion if you refer to the diagrams of stack
structure in Chapter 21, Object files and friends, page 377.
movl 0xc(%ebp),%edx takes the content of the stack word offset 12 in the current stack
frame and places it in register edx. As we have seen, this is num_src1, the second
This is, of course, wrong: the parameter sequence of source and destination has been reversed.
Lets look at _XtMemmove more carefully:
(gdb) l _XtMemmove
89 #ifdef _XNEEDBCOPYFUNC
90 void _XtMemmove(dst, src, length)
91 char *dst, *src;
92 int length;
93 {
94 if (src < dst) {
95 dst += length;
96 src += length;
97 while (length--)
98 *--dst = *--src;
99 } else {
100 while (length--)
101 *dst++ = *src++;
102 }
103 }
104 #endif
Clearly the function parameters are the same as those of memmove, but the calling sequence
has reversed them. Weve found the problem, but we havent found whats causing it.
Aside: Debugging is not an exact science. Weve found our problem, though we still dont
know whats causing it. But looking back at Example 8-1, we see that the address for src on
entering _XtMemmove was the same as the address of table. That tells us as much as analyz-
ing the machine code did. This will happen again and again: after you find a problem, you
As you might have guessed, we now look at the file junk.c with an editor. Were looking for
memmove, of course. We find a definition in /usr/include/string.h, then later on we find, in
/X/X11/X11R6/xc/X11/Xfuncs.h,
#define memmove(dst,src,len) bcopy((char *)(src),(char *)(dst),(int)(len))
For some reason, the configuration files have decided that memmove is not defined on this sys-
tem, and have replaced it with bcopy (which is really not defined on this system). Then they
replace it with the substitute function _XBCOPYFUNC, almost certainly a preprocessor defini-
tion. It also defines the preprocessor variable _XNEEDBCOPYFUNC to indicate that _XtMem-
move should be compiled.
Unfortunately, we dont see what happens with _XNEEDBCOPYFUNC. The preprocessor dis-
cards all #ifdef lines. It does include #defines, however, so we can look for where
_XBCOPYFUNC is definedits in IntrinsicI.h, as the last #line directive before the definition
indicates.
#define _XBCOPYFUNC _XtMemmove
IntrinsicI.h also contains a number of definitions for XtMemmove, none of which are used in
the current environment, but all of which have the parameter sequence (dst, src, count).
bcopy has the parameter sequence (src, dst, count). Clearly, somebody has confused
something in this header file, and under certain rare circumstances the call is defined with the
incorrect parameter sequence.
Somewhere in here is a lesson to be learnt: this is a real bug that occurred in X11R6, patch
level 3, one of the most reliable and most portable software packages available, yet here we
have a really primitive bug. The real problem lies in the configuration mechanism: automated
configuration can save a lot of time in normal circumstances, but it can also cause lots of pain
if it makes incorrect assumptions. In this case, the environment was unusual: the kernel plat-
form was SCO UNIX, which has an old-fashioned library, but the library was GNU libc. This
caused the assumptions of the configuration mechanism to break down.
Lets look more carefully at the part of Xfuncs.h where we found the definitions:
/* the new Xfuncs.h */
This is hairy (and incorrect) stuff. It makes its decisions based on the variables
X_NOT_STDC_ENV, sun, SVR4, SYSV, luna, __sxg__ and _XBCOPYFUNC. These are the deci-
sions:
If the variable is not defined, it assumes ANSI C, unless this is a pre-SVR4 Sun machine.
Otherwise it checks the variables SYSV (for System V.3), luna, sun or __sxg__. If any
of these are set, it includes the file memory.h and defines memmove in terms of bcopy. If
_XBCOPYFUNC is defined, it redefines memmove as _XBCOPYFUNC, reversing the parame-
ters as it goes.
If none of these conditions apply, it assumes a vanilla BSD machine and defines the func-
tions memmove, memcpy and memcmp in terms of the BSD functions bcopy and bcmp.
There are two errors here:
The only way that _XBCOPYFUNC is ever defined is as _XtMemmove, which does not have
the same parameter sequence as bcopyinstead, it has the same parameter sequence as
memmove. We can fix this part of the header by changing the definition line to
#define memmove(dst,src,len) _XBCOPYFUNC((char *)(dst),(char *)(src),(int)(len))
or even to
#define memmove _XBCOPYFUNC
There is no reason to assume that this system does not use ANSI C: its using gcc and
GNU libc.a, both of them very much standard compliant. We need to examine this point
in more detail:
Going back to our junk.c, we search for X_NOT_STDC_ENV and find it defined at line 85 of
/X/X11/X11R6/xc/X11/Xosdefs.h:
#ifdef SYSV386
#ifdef SYSV
#define X_NOT_POSIX
#define X_NOT_STDC_ENV
#endif
#endif
In other words, this bug is likely to occur only with System V.3 implementations on Intel
architecture. This is a fairly typical way to make decisions about the system, but it is wrong:
X_NOT_STDC_ENV relates to a compiler, not an operating system, but both SYSV386 and SYSV
define operating system characteristics. At first sight it would seem logical to modify the defi-
nitions like this:
#ifdef SYSV386
#ifdef SYSV
#ifndef __GNU_LIBRARY__
#define X_NOT_POSIX
#endif
#ifndef __GNUC__
#define X_NOT_STDC_ENV
#endif
#endif
#endif
This would only define the variables if the library is not GNU libc or the compiler is not gcc.
This is still not correct: the relationship between __GNUC__ and X_NOT_STDC_ENV or
__GNU_LIBRARY__ and X_NOT_POSIX is not related to System V or the Intel architecture.
Instead, it makes more sense to backtrack at the end of the file:
#ifdef __GNU_LIBRARY__
#undef X_NOT_POSIX
#endif
#ifdef __GNUC__
#undef X_NOT_STDC_ENV
#endif
Whichever way we look at it, this is a mess. Were applying cosmetic patches to a
configuration mechanism which is based in incorrect assumptions. Until some better configu-
ration mechanism comes along, unfortunately, were stuck with this situation.
Limitations of debuggers
Debuggers are useful tools, but they have their limitations. Here are a couple which could
cause you problems:
This example was done on a BSD machine. On a System V machine you will need to use ps
-ef instead of ps aux. First, you start an xterm with sleep as controlling shell (so that it will
stay there). With ps you grep for the controlling terminal of the sleep process (the third line in
the example), and then you start your program with stdin and stdout redirected to this termi-
nal.
trace
trace is a relatively primitive tool supplied with SunOS 4 systems. It can either start a process
or attach to an existing process, and it can print summary information or a detailed trace. In
particular, it cannot trace the child of a fork call, which is a great disadvantage. Heres an
example of trace output with a possibly recognizable program:
$ trace hello
open ("/usr/lib/ld.so", 0, 040250) = 3
read (3, "".., 32) = 32
mmap (0, 40960, 0x5, 0x80000002, 3, 0) = 0xf77e0000
mmap (0xf77e8000, 8192, 0x7, 0x80000012, 3, 32768) = 0xf77e8000
open ("/dev/zero", 0, 07) = 4
getrlimit (3, 0xf7fff488) = 0
mmap (0xf7800000, 8192, 0x3, 0x80000012, 4, 0) = 0xf7800000
close (3) = 0
getuid () = 1004
getgid () = 1000
open ("/etc/ld.so.cache", 0, 05000100021) = 3
fstat (3, 0xf7fff328) = 0
mmap (0, 4096, 0x1, 0x80000001, 3, 0) = 0xf77c0000
close (3) = 0
open ("/opt/lib/gcc-lib/sparc-sun-sunos".., 0, 01010525) = 3
fstat (3, 0xf7fff328) = 0
getdents (3, 0xf7800108, 4096) = 212
getdents (3, 0xf7800108, 4096) = 0
close (3) = 0
open ("/opt/lib", 0, 056) = 3
getdents (3, 0xf7800108, 4096) = 264
getdents (3, 0xf7800108, 4096) = 0
close (3) = 0
open ("/usr/lib/libc.so.1.9", 0, 023170) = 3
read (3, "".., 32) = 32
mmap (0, 458764, 0x5, 0x80000002, 3, 0) = 0xf7730000
mmap (0xf779c000, 16384, 0x7, 0x80000012, 3, 442368) = 0xf779c000
close (3) = 0
open ("/usr/lib/libdl.so.1.0", 0, 023210) = 3
read (3, "".., 32) = 32
mmap (0, 16396, 0x5, 0x80000002, 3, 0) = 0xf7710000
mmap (0xf7712000, 8192, 0x7, 0x80000012, 3, 8192) = 0xf7712000
close (3) = 0
close (4) = 0
getpagesize () = 4096
brk (0x60d8) = 0
brk (0x70d8) = 0
ioctl (1, 0x40125401, 0xf7ffea8c) = 0
write (1, "Hello, World!0, 14) = Hello, World!
14
close (0) = 0
close (1) = 0
close (2) = 0
exit (1) = ?
Whats all this output? All we did was a simple write, but we have performed a total of 43
system calls. This shows in some detail how much the viewpoint of the world differs when
youre on the other side of the system library. This program, which was run on a SparcStation
2 with SunOS 4.1.3, first sets up the shared libraries (the sequences of open, read, mmap, and
close), then initializes the stdio library (the calls to getpagesize, brk, ioctl, and
fstat), and finally writes to stdout and exits. It also looks strange that it closed stdin before
writing the output text: again, this is a matter of perspective. The stdio routines buffer the
text, and it didnt actually get written until the process exited, just before closing stdout.
ktrace
ktrace is supplied with newer BSD systems. Unlike the other trace programs, it writes unfor-
matted data to a log file (by default, ktrace.out), and you need to run another program, kdump,
to display the log file. It has the following options:
It can trace the descendents of the process it is tracing. This is particularly useful when
the bug occurs in large complexes of processes, and you dont even know which process
is causing the problem.
It can attach to processes that are already running. Optionally, it can also attach to exist-
ing children of the processes to which it attaches.
It can specify broad subsets of system calls to trace: system calls, namei translations
(translation of file name to inode number), I/O, and signal processing.
Heres an example of ktrace running against the same program:
$ ktrace hello
Hello, World!
$ kdump
20748 ktrace RET ktrace 0
20748 ktrace CALL getpagesize
20748 ktrace RET getpagesize 4096/0x1000
20748 ktrace CALL break(0xadfc)
20748 ktrace RET break 0
20748 ktrace CALL break(0xaffc)
20748 ktrace RET break 0
20748 ktrace CALL break(0xbffc)
20748 ktrace RET break 0
20748 ktrace CALL execve(0xefbfd148,0xefbfd5a8,0xefbfd5b0)
20748 ktrace NAMI "./hello"
20748 hello RET execve 0
20748 hello CALL fstat(0x1,0xefbfd2a4)
20748 hello RET fstat 0
20748 hello CALL getpagesize
20748 hello RET getpagesize 4096/0x1000
20748 hello CALL break(0x7de4)
20748 hello RET break 0
20748 hello CALL break(0x7ffc)
20748 hello RET break 0
20748 hello CALL break(0xaffc)
20748 hello RET break 0
20748 hello CALL ioctl(0x1,TIOCGETA,0xefbfd2e0)
20748 hello RET ioctl 0
20748 hello CALL write(0x1,0x8000,0xe)
20748 hello GIO fd 1 wrote 14 bytes
"Hello, World!
"
20748 hello RET write 14/0xe
20748 hello CALL exit(0xe)
truss
truss, the System V.4 trace facility, offers the most features:
It can print statistical information instead of a trace.
It can display the argument and environment strings passed to each call to exec.
It can trace the descendents of the process it is tracing.
Like ktrace, it can attach to processes which are already running and optionally attach to
existing children of the processes to which it attaches.
It can trace specific system calls, signals, and interrupts (called faults in System V termi-
nology). This is a very useful feature: as we saw in the ktrace example above, the C
library may issue a surprising number of system calls.
Heres an example of truss output:
$ truss -f hello
511: execve("./hello", 0x08047834, 0x0804783C) argc = 1
511: getuid() = 1004 [ 1004 ]
511: getuid() = 1004 [ 1004 ]
511: getgid() = 1000 [ 1000 ]
511: getgid() = 1000 [ 1000 ]
511: sysi86(SI86FPHW, 0x80036058, 0x80035424, 0x8000E255) = 0x00000000
511: ioctl(1, TCGETA, 0x08046262) = 0
Hello, World!
511: write(1, " H e l l o , W o r l d".., 14) = 14
511: _exit(14)
truss offers a lot of choice in the amount of detail it can display. For example, you can select
a verbose parameter display of individual system calls. If were interested in the parameters
to the ioctl call, we can enter:
$ truss -f -v ioctl hello
...
516: ioctl(1, TCGETA, 0x08046262) = 0
In this case, truss shows the contents of the termio structure associated with the TCGETA
request see Chapter 15, Terminal drivers, pages 241 and 258, for the interpretation of this
information.
125
make install
The traditional way to install a pre-compiled package is with make install. Typically, it per-
forms the following functions:
It creates the necessary directories if they are not there.
It copies all necessary files to their run-time locations.
It sets the permissions of the files to their correct values. This frequently requires you to
be root when you install the package. If you dont have root access, you should at least
arrange for access to the directories into which you want to install.
It may strip debug information from executables.
Some other aspects of make install are less unified:
make install may imply a make all: you cant install until you have made the package,
and youll frequently see an install target that starts with
install: all
installation commands
On the other hand, make install may not only expect the make all to be completedand
fail if it is notbut remove the executables after installation. Sometimes this is due to
the use of BSD install without the -c option see the section on the install program
belowbut it means that if you want to make a change to the program after installation,
you effectively have to repeat the whole build. Removing files from the tree should be
left to make clean (see Chapter 5, Building the package, page 63).
Some install targets install man pages or other on-line documentation, others leave it to a
separate target with a name like install-man, and yet other Makefiles completely
ignore online documentation, even if the package supplies it.
The reasons for this behaviour are shrouded in time, but may be related to the fact that both
install (which we will discuss below) and cp traditionally modify the time stamps of the files,
so that the following scenario could occur:
1. Build version 1 of a package, and install it.
2. Start building version 2, but dont complete it.
3. Make a modification to version 1, and re-install it.
4. Complete version 2, and install it. Some of the file in version 2 were compiled before
version 1 was re-installed, and are thus older than the installed files. As a result, they
will not be installed, and the installed software will be inconsistent.
Its obviously safer to replace everything. But is that enough? Well look at the opposite prob-
lem in the next section.
Updating
Frequently you will install several versions of software over a period of time, as the package
evolves. Simply installing the new version on top of the old version will work cleanly only if
you can be sure that you install a new version of every file that was present in the old version:
otherwise some files from the old version will remain after installation. For example, version
1.07.6 of the GNU libc included a file include/sys/bitypes.h, which is no longer present in ver-
sion 1.08.6. After installing version 1.08.6, include/sys/bitypes.h is still present from the ear-
lier installation.
The correct way to handle this problem is to uninstall the old package before installation. For
reasons we will investigate on page 133, this seldom happens.
install
install is a program that is used in installing software. It performs the tasks of creating any
necessary directories, copying files, stripping executables, and setting permissions.
install originated in Berkeley, and older System V systems dont support it. Its a fairly trivial
program, of course, and many packages supply a script with a name like install.sh which per-
forms substantially the same functions. The source is available in the 4.4BSD Lite distribu-
tion see Appendix E, Where to get sources.
Although install is a relatively simple program, it seems that implementors have done their
best to make it differ from one system to the next. The result is a whole lot of incompatible
and just downright confusing options. System V.4 even supplies two different versions with
conflicting options, a BSD compatible one and a native one the one you get depends on
your other preferences, as laid down in your PATH environment variable.
System V.4 native install is sufficiently different from the others that we need to look at it sep-
arately it can install only a single file. The syntax is:
If the dirs are specified, they are appended to the fixed list of directories /bin, /usr/bin, /etc,
/lib, and /usr/lib. install will search the resultant list of directories sequentially for a file with
the name file. If it finds one, it will replace it and print a message stating in which directory it
has installed the file. The -i option tells install to omit the standard directories and take only
the list specified on the command line.
Other versions of install have a syntax similar to mv and cp, except that they take a number of
supplementary options:
install options file1 file2
install options file1 ... fileN dir
The first form installs file1 as file2, the second form installs file1 through fileN in the directory
dir.
Table 9-1 contains an overview of install options:
option Purpose
-c In BSD, copy the file. If this option is not specified, the file is moved (the origi-
nal file is deleted after copying).
In GNU and System V.4 (BSD compatibility), this option is ignored. Files are
always copied.
-c dir System V.4 native: install the file in directory dir only if the file does not already
exist. If the file exists already, exit with an error message.
-d In GNU and SunOS, create all necessary directories if the target directory does
not exist. Not available in BSD. This lets you create the directory with the com-
mand
install -d [-g group] [-m perm] [-o owner] dir
-f flags In 4.4BSD, specify the targets file flags. This relates to the chflags program in-
troduced with 4.4BSDsee the man page usr.bin/chflags/chflags.1 in the
4.4BSD Lite distribution.
-f dir System V.4 native: force the file to be installed in dir. This is the default for oth-
er versions.
-g group Set the group ownership to group.
-i System V.4 native: ignore the default directory list (see below). This is not ap-
plicable with the -c or -f options.
-m perm Set the file permissions to perm. perm may be in octal or symbolic form, as de-
fined for chmod(1). By default, perm is 0755 (rwxr-xr-x).
Installing documentation
Installing man pages would seem to be a trivial exercise. In fact, a number of problems can
occur. In this section, well look at problems you might encounter installing man pages and
GNU info.
Man pages.
As we saw in Chapter 7, Documentation, page 99, there is not much agreement about naming,
placing, or format of man pages. In order to install man pages correctly you need to know the
following things:
The name of the man directory.
The naming convention for man files. As we saw, these are many and varied.
Whether the man pages should be formatted or not.
If the man pages should be formatted, which formatter should be used? Which macros
should be used? This may seem like a decision to be made when building the package,
but many Makefiles put off this operation to the install phase.
Whether the man pages should be packed, compressed or zipped.
Typically, this information is supplied in the Makefile like this example from the electronic
mail reader elm, which is one of the better ones:
FORMATTER = /usr/ucb/nroff
MAN = /opt/man/man1
MANEXT = .1
CATMAN = /opt/man/cat1
CATMANEXT = .1
TBL = /usr/ucb/tbl
MANROFF = /usr/ucb/nroff
SUFFIX = .Z
PACKED = y
PACKER = /bin/compress
# Targets
all:
@if $(TEST) $(CATMAN) != none; then $(MAKE) formatted_pages ; \
else true ; fi
catman:
mkdir catman
install: $(LIB_LIST)
@if $(TEST) $(MAN) != none; then $(MAKE) install_man ; \
else true ; fi
@if $(TEST) $(CATMAN) != none; then $(MAKE) install_catman ; \
else true ; fi
# Dependencies and rules for installing man pages and lib files
$(MAN)/answer$(MANEXT): answer.1
$(CP) $? $@
$(CHMOD) u=rw,go=r $@
$(MAN)/autoreply$(MANEXT): autoreply.1
$(CP) $? $@
$(CHMOD) u=rw,go=r $@
This Makefile is in the subdirectory doc, which is concerned only with documentation, so all
the targets relate to the man pages. The target all makes the decision whether to format the
pages or not based on the value of the make variable CATMAN. If this is set to the special value
none, the Makefile does not format the pages.
The target install uses the same technique to decide which man pages to install: if the vari-
able MAN is not set to none, the sources of the man pages are copied there, and if CATMAN is
not set to none, the formatted pages are installed there. This Makefile does not use install: it
performs the operations with cp and chmod instead.
GNU info
Installing GNU info is somewhat more straightforward, but it is also not as clean as it could
be:
info is always formatted, so you need the formatter, a program called makeinfo, which is
part of the texinfo package. Before you can run makeinfo, you need to port texinfo. Its
not that big a job, but it needs to be done. Of course, in order to completely install tex-
info, you need to format the documentation with makeinfoa vicious circle. The solu-
tion is to port the texinfo executables, then port makeinfo, and then format the texinfo
documentation.
All info files are stored in a single directory with an index file called dir. This looks like:
-*- Text -*-
This is the file /opt/info/dir, which contains the topmost node of the
Info hierarchy. The first time you invoke Info you start off
looking at that node, which is (dir)Top.
File: dir Node: Top This is the top of the INFO tree
This (the Directory node) gives a menu of major topics.
Typing "d" returns here, "q" exits, "?" lists all INFO commands, "h"
gives a primer for first-timers, "mTexinfo<Return>" visits Texinfo topic,
etc.
Note that the presence of a name in this list does not necessarily
mean that the documentation is available. It is installed with the
package in question. If you get error messages when trying to access
documentation, make sure that the package has been installed.
--- PLEASE ADD DOCUMENTATION TO THIS TREE. (See INFO topic first.) ---
...etc
The lines at the bottom of the example are menu entries for each package. They have a
syntax which isnt immediately apparentin particular, the sequence * item: has a
special significance in emacs info mode. Programs that supply info documentation
should supply such an entry, but many of them do not, and none of them install the line
in diryou need to do this by hand.
-mkdir $(docdir)
for f in NEWS devices.doc drivers.doc fonts.doc hershey.doc \
history.doc humor.doc language.doc lib.doc make.doc ps2epsi.doc \
psfiles.doc readme.doc use.doc xfonts.doc ; \
do $(INSTALL_DATA) $$f $(docdir)/$$f ; done
-mkdir $(mandir)
for f in ansi2knr.1 gs.1 ; do $(INSTALL_DATA) $$f $(mandir)/$$f ; done
-mkdir $(exdir)
for f in chess.ps cheq.ps colorcir.ps golfer.ps escher.ps \
snowflak.ps tiger.ps ; \
do $(INSTALL_DATA) $$f $(exdir)/$$f ; done
One alternative is to make a remove target for this Makefile, which isnt too difficult in this
case:
First, copy the install target and call it remove.
Move the mkdir lines to the bottom and change them to rmdir. Youll notice that this
Makefile accepts the fact that mkdir can fail because the directory already exists (the - in
front of mkdir). Well do the same with rmdir: if the directory isnt empty, rmdir fails,
but thats OK.
We replace $(INSTALL_PROGRAM) $$f and $(INSTALL_DATA) $$f with rm -f.
The result looks like:
remove: $(GS)
for f in $(GS) gsbj gsdj gslj gslp gsnd bdftops font2c \
ps2ascii ps2epsi; \
do rm -f $(bindir)/$$f ; done
-rmdir $(exdir)
More frequently, however, you cant use this approach: the Makefile isnt as easy to find, or
you have long since discarded the source tree. In this case, well have to do it differently.
First, we find the directory where the executable gs, the main ghostscript program, is stored:
$ which gs
/opt/bin/gs
This is to help us to know where to look in the next step: we list the directory /opt/bin sorted
by modification timestamp. Its a lot easier to find what were looking for if we know the
date. If you dont have which, or possibly even if you do, you can use the following script,
called wh:
for j in $*; do
for i in echo $PATH|sed s/:/ /g; do
if [ -f $i/$j ]; then
ls -l $i/$j
fi
done
done
wh searches the directories in the current environment variable PATH for a specific file and
lists all occurrences in the order in which they appear in PATH in ls -l format, so you could
also have entered:
$ wh gs
-rwxrwxr-x 1 root wheel 3168884 Jun 18 14:29 /opt/bin/gs
Once we know the date we are looking for, its easy to list the directory, page it through more
and find the time frame we are looking for.
$ ls -lt /opt/bin|more
total 51068
-rw------- 1 root bin 294912 Sep 6 15:08 trn.old
-rwxr-xr-x 1 grog lemis 106496 Sep 6 15:08 man
...skipping lots of stuff
-rw-rw-rw- 1 grog bin 370 Jun 21 17:24 prab
-rw-rw-rw- 1 grog bin 370 Jun 21 17:22 parb
-rw-rw-rw- 1 grog bin 196 Jun 21 17:22 parb
-rwxrwxrwx 1 grog wheel 469 Jun 18 15:19 tep
-rwxrwxr-x 1 root wheel 52 Jun 18 14:29 font2c
-rwxrwxr-x 1 root wheel 807 Jun 18 14:29 ps2epsi
-rwxrwxr-x 1 root wheel 35 Jun 18 14:29 bdftops
-rwxrwxr-x 1 root wheel 563 Jun 18 14:29 ps2ascii
-rwxrwxr-x 1 root wheel 50 Jun 18 14:29 gslp
-rwxrwxr-x 1 root wheel 3168884 Jun 18 14:29 gs
-rwxrwxr-x 1 root wheel 53 Jun 18 14:29 gsdj
-rwxrwxr-x 1 root wheel 51 Jun 18 14:29 gsbj
Its easy to recognize the programs in this format: they were all installed in the same minute,
and the next older file (faxaddmodem) is more than 90 minutes older, the next newer file (tep)
is 50 minutes newer. The files we want to remove are, in sequence, font2c, ps2epsi, bdftops,
ps2ascii, gslp, gs, gsdj, gsbj, gsnd and gslj.
Were not done yet, of course: ghostscript also installs a lot of fonts and PostScript files, as we
saw in the Makefile. How do we find and remove them? It helps, of course, to have the Make-
file, from which we can see that the files are installed in the directories /opt/bin,
/opt/lib/ghostscript and /opt/man/man1 (see the Makefile excerpt on page 133). If you dont
have the Makefile, all is not lost, but things get a little more complicated. You can search the
complete directory tree for files modified between Jun 18 14:00 and Jun 18 14:59 with:
$ find /opt -follow -type f -print|xargs ls -l|grep "Jun 18 14:"
-rwxrwxr-x 1 root wheel 35 Jun 18 14:29 /opt/bin/bdftops
...etc
-rw-rw-r-- 1 root wheel 910 Jun 18 14:29 /opt/man/man1/ansi2knr.1
-rw-rw-r-- 1 root wheel 10005 Jun 18 14:29 /opt/man/man1/gs.1
-rw-rw-r-- 1 root wheel 11272 Jun 18 14:29 /opt/lib/ghostscript/Fontmap
-rw-rw-r-- 1 root wheel 22789 Jun 18 14:29 /opt/lib/ghostscript/bdftops.ps
-rw-rw-r-- 1 root wheel 295 Jun 18 14:29 /opt/lib/ghostscript/decrypt.ps
-rw-rw-r-- 1 root wheel 74791 Jun 18 14:29 /opt/lib/ghostscript/doc/NEWS
-rw-rw-r-- 1 root wheel 13974 Jun 18 14:29 /opt/lib/ghostscript/doc/devices.doc
...many more files
This may be enough to differentiate between the files, but its less certain. GNU ls (in
the fileutils package) includes a option -full-time (note the two leading hyphens).
This will always print the full time, regardless of the age of the file. With this option, the
file above will list as:
$ ls --full-time -l xyzzy
-rwxrwxrwx 1 grog wheel 22 Thu Feb 10 16:00:24 1994 xyzzy
System V pkgadd
UNIX System V.4 is supplied as a number of binary packages* you can choose which to
install and which not to install. You can even choose whether or not to install such seemingly
essential components as networking support and man pages.
Packages can be created in two formats: stream format for installation from serial data media
like tapes, and file system format for installation from file systems. In many cases, such as
diskettes, either form may be used. The program pkgtrans transforms one format into the
other. In the following discussion, well assume file system format.
The package tools offer a bewildering number of options, most of which are not very useful.
Well limit our discussion to standard cases: in particular, we wont discuss classes and multi-
part packages. If you are using System V.4 and want to use other features, you should read
the documentation supplied with the system. In the following sections well look at the indi-
vidual components of the packages.
pkginfo
The file pkginfo, in the root directory of the package, contains general information about the
package, some of which may be used to decide whether or not to install the package. For
example, the pkginfo file for an installable emacs package might look like:
ARCH=i386 the architecture for which the package is intended
PKG=emacs the name of the package
VERSION=19.22 the version number
NAME=Emacs text editor a brief description
CATEGORY=utilities the kind of package
CLASSES=none class information
VENDOR=Free Software Foundation the name of the owner
HOTLINE=LEMIS, +49-6637-919123, Fax +49-6637-919122 who to call if you have trouble
[email protected] mail for HOTLINE
pkgmap
The file pkgmap is also in the root directory of the package. It contains information about the
destination of the individual files. For example, from the same emacs package,
: 1 37986
1 d none /opt 0755 bin bin
1 d none /opt/README 0755 bin bin
1 f none /opt/README/emacs-19.22 0644 root sys 1518 59165 760094611
1 d none /opt/bin 0755 bin bin
1 f none /opt/bin/emacs 0755 root sys 1452488 11331 760577316
1 f none /opt/bin/etags 0755 root sys 37200 20417 760577318
* As used here, the term package is a collection of precompiled programs and data and information nec-
essary to install themthis isnt the same thing as the kind of package we have been talking about in
the rest of this book.
The first line specifies that the package consists of a single part, and that it consists of 37986
512 byte blocks. The other lines describe files or directories:
The first parameter is the part to which the file belongs.
The next parameter specifies whether the file is a plain file (f), a directory (d), a link (l)
or a symbolic link (s). A number of other abbreviations are also used.
The next parameter is the class of the file. Like most packages, this package does not
use classes, so the class is always set to none.
The following four parameters specify the name of the installed object, its permissions,
the owner and the group.
After this come the size of the file, a checksum and the modification time in naked
time_t format. The checksum ensures that the package is relatively protected against
data corruption or deliberate modification.
Package subdirectories
In addition to the files in the main directory, packages contain two subdirectories root and
install:
root contains the files that are to be installed. All the files described in pkgmap are
present under the same names in root (for example, /opt/bin/emacs is called
root/opt/bin/emacs in the package).
The file install/copyright contains a brief copyright notice that is displayed on installa-
tion. pkgadd does not wait for you to read this, so it should really be brief.
Optionally, there may be scripts with names like install/preinstall and install/postinstall
which are executed before and after copying the files, respectively. preinstall might, for
example, set up the directory structure /opt if it does not already exist. postinstall might
update .cshrc and .profile files. In some cases, it may need to do more. For example, the
ISO 9660 directory standard for CD-ROMs limits allows only eight nested directories (in
other words, the directory /a/b/c/d/e/f/g/h/i is nested too deeply). gcc on a CD-ROM
would violate this limitation, so some of the package has to be stored as a tar file, and the
postinstall script extracts it to the correct position.
pkgadd
With this structure, adding a package is almost childs play: you just have to enter
$ pkgadd emacs
Well, almost. The name emacs is the name of the package and not a file name. By default,
pkgadd expects to find it in /var/spool/pkg. If your package is elsewhere, you cant tell
pkgadd simply by prepending the nameinstead, you need to specify it with the -d option:
$ pkgadd -d /cdrom emacs
Removing packages
One really nice thing about the System V.4 package system is the ease with which you can
remove a package. Assuming that you have decided that vi is a better choice than emacs, or
you just dont have the 19 MB that the emacs package takes up, you just have to type:
$ pkgrm emacs
which will tell it to search the directory /tmp-opt and generate entries for /opt. The dis-
advantage of this approach is that you may end up building programs with the path /tmp-
opt hard coded into the executables, and though it may test just fine on your system, the
executable files will not work on the target systemdefinitely a situation to avoid.
You rename /opt temporarily and install emacs in a new directory, which you can then
rename. This virtually requires you to be the only user on the system.
Before installing emacs, you create a dummy file stamp-emacs just about anywhere on
the system. Then you install emacs, and make a list of the files you have just installed:
$ find /opt -follow -cnewer stamp-emacs -type f -print | xargs ls -l >info
This requires you to be the only person on the system who can write to the directory at
the time. This is more not as simple as you might think. Mail and news can come in
even if nobody else is using the system. Of course, they wont usually write in the same
directories that youre looking in. Nevertheless, you should be prepared for a few sur-
prises. For example, you might find a file like this in your list:
/opt/lib/emacs/lock/!cdcopy!SOURCE!Core!glibc-1.07!version.c
This is an emacs lock file: it is created by emacs when somebody modifies a buffer (in
this case, a file called /cdcopy/SOURCE/Core/glibc-1.07/version.c: emacs replaces the
slashes in the file name by exclamation marks), and causes another emacs to warn the
user before it, too, tries to modify the same file. It contains the pid of the emacs process
that has the modified buffer. Obviously you dont want to include this file in your instal-
lable package.
Once you have tidied up your list of files, you can generate a prototype file with the aid
of a shell script or an editor.
Running pkgmk
Once you have a prototype file, youre nearly home. All you have to do is run pkgmk. We run
into terminology problems here: throughout this book, we have been using the term package
to refer to the software we are building. More properly, this is the software package. pkgmk
refers to its output as a package toohere, well refer to it as the installable package.
Unfortunately, pkgmk handles some pathnames strangely. You can read the man page (prefer-
ably several times), or use this method, which works:
Before building the installable package, change to the root directory of the software
package.
Ignore path specifications in the prototype file and specify the root path as the root file
system: -r /.
Specify the base directory as the root directory of the package: since thats the directory
were in, just add -b pwd.
Choose to overwrite any existing package: -o.
Specify the destination path explicitly: -d /usr/pkg. pkgmk creates packages will as
subdirectories in this directory: the package gcc would create a directory hierarchy
/usr/pkg/gcc.
The resultant call doesnt change from one package to the next: it is
pkgmk -r / -b pwd -o -d /usr/pkg
There is a whole lot more to using pkgmk, of course, but if you have pkgmk, you will also
have the man pages, and thats the best source of further information.
Reporting modifications
Once you have the software running, you should report any changes to the author or main-
tainer of the software. In order for this to be of any use, you need to supply the following
information:
A description of the problems you ran into. Dont spare details here: remember the pain
you went to to figure out what was going wrong, and you had an interest in solving the
problem. If youre the first person to run into the problem, it probably hasnt hurt any-
body else, least of all the author. He probably gets lots of mail saying xfoo is broke,
and he may not believe what you have to say until you prove it to him.
How you fixed them. Again, lots of detail. The author probably understands the package
better than you do. If you explain the problem properly, he may come up with a better
143
fix.
The fixes themselves. diffs, lists of differences between the previous version and your
versions, are the method of choice. Well look at them in the rest of this section.
diff
diff is a program that compares two related source files and outputs information about how to
create the second file from the first. You typically use it after making modifications to a file in
order to describe how the modified file differs from the original. The resultant output file is
also called a diff. We saw the application of diffs in Chapter 3, Care and feeding of source
trees, page 29. Here well look at how to make them.
Its useful to recognize and understand diff formats, since you occasionally have to apply
them manually. diff compares two source files and attempts to output a reasonably succinct
list of the differences between them. In diff terminology, the output is grouped into hunks,
information about a relatively local groups of differences.
Like most useful programs, diff has grown in the course of time, and modern versions can out-
put in a bewildering number of formats. Fortunately, almost all diffs nowadays use the con-
text format. Well look at some others anyway so that you can recognize them.
In the following examples, we compare the files eden.1:
A doctor, an architect, and a computer scientist
were arguing about whose profession was the oldest. In the
course of their arguments, they got all the way back to the
Garden of Eden, whereupon the doctor said, "The medical
profession is clearly the oldest, because Eve was made from
Adams rib, as the story goes, and that was a simply
incredible surgical feat."
The architect did not agree. He said, "But if you
look at the Garden itself, in the beginning there was chaos
and void, and out of that, the Garden and the world were
created. So God must have been an architect."
The computer scientist, who had listened to all of
this said, "Yes, but where do you think the chaos came
from?"
and eden.2:
A doctor, an architect, and a computer scientist
were arguing about whose profession was the oldest. In the
course of their arguments, they came to discuss the Garden
of Eden, whereupon the doctor said, "The medical profession
is clearly the oldest, because Eve was made from Adams rib,
as the story goes, and that was a simply incredible surgical
feat."
The architect did not agree. He said, "But if you
look at the Garden itself, in the beginning there was chaos
and void, and out of that, the Garden and the world were
created. So God must have been an architect."
The computer scientist, who had listened to all of
this, said, "Yes, but where do you think the chaos came
from?"
The first line of each hunk specifies the line range: 3,7c3,7 means lines 3 to 7 of the first
file, lines 3 to 7 of the second file. 13c13 means line 13 of the first file, line 13 of the sec-
ond file, has changed (c). Instead of c you will also see d (lines deleted) and a (lines added).
After this header line come the lines of the first file, with a leading < character, then a divider
(---) and the lines of the second file with a leading > character. This example has two hunks.
ed format diffs
ed format diffs have the dubious advantage that the program ed can process them. You can
create them with the -e flag. In this example, we also use shell syntax to shorten the input
line. Writing eden.[12] is completely equivalent to writing eden.1 eden.2.
$ diff -e eden.[12]
13c
this, said, "Yes, but where do you think the chaos came
.
3,7c
course of their arguments, they came to discuss the Garden
of Eden, whereupon the doctor said, "The medical profession
is clearly the oldest, because Eve was made from Adams rib,
as the story goes, and that was a simply incredible surgical
feat."
.
Just about everybody who has diff also has patch, and nowadays not everybody has ed. In
addition, this format is extremely dangerous, since there is no information about the old
content of the file: you cant be sure if the patch will be applied in the right place. As a result,
you almost never see this form.
context diffs
You select a context diff with the flag -c:
$ diff -c eden.[12]
*** eden.1 Tue May 10 14:21:47 1994
--- eden.2 Tue May 10 14:22:38 1994
***************
*** 1,14 ****
A doctor, an architect, and a computer scientist
were arguing about whose profession was the oldest. In the
! course of their arguments, they got all the way back to the
! Garden of Eden, whereupon the doctor said, "The medical
! profession is clearly the oldest, because Eve was made from
! Adams rib, as the story goes, and that was a simply
! incredible surgical feat."
The architect did not agree. He said, "But if you
look at the Garden itself, in the beginning there was chaos
and void, and out of that, the Garden and the world were
created. So God must have been an architect."
The computer scientist, who had listened to all of
! this said, "Yes, but where do you think the chaos came
from?"
--- 1,14 ----
A doctor, an architect, and a computer scientist
were arguing about whose profession was the oldest. In the
! course of their arguments, they came to discuss the Garden
! of Eden, whereupon the doctor said, "The medical profession
! is clearly the oldest, because Eve was made from Adams rib,
! as the story goes, and that was a simply incredible surgical
! feat."
The architect did not agree. He said, "But if you
look at the Garden itself, in the beginning there was chaos
and void, and out of that, the Garden and the world were
created. So God must have been an architect."
The computer scientist, who had listened to all of
! this, said, "Yes, but where do you think the chaos came
The output here gives us significantly more information: the first two line gives the name and
modification timestamp of the files. Then the hunks start, with a row of * as a leader. The
next line is line number information for the first file (lines 1 to 14), after which come the lines
themselves, surrounded by a number of lines of context, unchanged information. You can
specify the number of lines of context, but by default diff includes 2 lines either side of the
changes. The lines that have been modified are flagged with an exclamation mark (!) at the
beginning of the line. In this case, the file is so small that the two modifications have been
merged into one large one, and the whole file gets repeated, but in a larger file diff would
include only the information immediately surrounding the changes. This format is more reli-
able than normal diffs: if the original source file has changed since the diff, the context
As with context diffs, there is a header with information about the two files, followed by a
hunk header specifying the line number range in each of the two files. Unlike a normal con-
text diff, the following hunk contains the old text mingled with the new text. The lines pre-
fixed with the character - belong to the first file, those prefixed with + belong to the second
file in other words, to convert the old file to the new file you remove the lines prefixed with
- and insert the lines prefixed with +.
There are still other formats offered by various flavours of diff, but these are the only impor-
tant ones.
another, you should go to the trouble to report problems you experience, even if you cant fix
them and there is no support obligation.
A final word: if you give up on a port after getting this far, this book has failed for you. I
dont want that to happen. Please contact me, too ([email protected], or via OReilly and As-
sociates) and explain the problem. Like the authors of the software, I dont guarantee to do
anything about it, but I might, and your experience might help to make the next edition of this
book more useful.
Software Dependencies
Probably the biggest problem you will have with configuration will be with the underlying
software platform. Even if you limit your scope to the various UNIX versions, 25 years of
continuing (and mainly uncoordinated) evolution have left behind a plethora of marginally
compatible versions. The only good thing about the situation is that porting between UNIX
versions is still an order of magnitude easier than porting to or from a non-UNIX environ-
ment.
Its easy to misjudge the effort required to port to a different platform. It helps to make the
following very clear distinctions between the following kinds of functionality:
Functionality that relies on system calls (section 2 of the UNIX manual). These calls
interface directly with the kernel. If the kernel doesnt supply the functionality, you may
have serious difficulty in porting the product. Good examples are the System V function
shmget, which allocates an area of shared memory, or the BSD system call symlink,
which creates a symbolic link.
Functionality dependent on system library calls (section 3 of the UNIX manual). If these
do not rely on system calls, you may be able to port a corresponding call from another
library. A good example of this is the function strcasecmp, which compares strings
ignoring case. This function is supplied with later versions of the BSD library and also
with GNU libc, but not with System V libraries. If you dont have it, its trivial to port.
151
Functionality contained totally inside the package, like math routines that dont call
external libraries. This should work on any platform.
Some systems, such as OSF, have merged sections 2 and 3 of the manual pages. While that
has some advantages (if you know a function name, you dont have to go to two different
places to look for them), it doesnt mean that there is no longer a difference.
Kernel dependencies are significantly more difficult to handle than library dependencies, since
theres relatively little you can do about them. Well look at kernel-related problems in Chap-
ter 12, Kernel dependencies, Chapter 13, Signals, Chapter 14, File systems, Chapter 15, Ter-
minal drivers, and Chapter 16, Timekeeping. In Chapter 17 well look at header files, and in
Chapter 18 well look at libraries.
In addition to these program dependencies, two tools can differ significantly: the make pro-
gram and the C compiler. Well look at these aspects in Chapter 19, Make, and Chapter 20,
Compilers. Finally, in Chapter 21, Object files and friends, well look at some of the more
esoteric aspects of object files.
When discussing differences between kernels and libraries, the big difference is usually
between System V and BSD, with other systems such as SunOS taking a midfield position.
System V.4 incorporates nearly everything in BSD. When programming, you have the choice
between using the native System V development tools or the BSD tools. Some admixture is
possible, but it can cause problems.
When using BSD development tools, everything that is supported by BSD should also be sup-
ported by System V.4. On the other hand, System V.4 also includes some functionality that no
other system provides. When, in the following chapters, I say that a function is supported by
System V.4, I mean that it is supported by System V.4 using the standard development tools
and libraries. If I state that it is supported by BSD, it also implies that it is supported by Sys-
tem V.4 using the BSD libraries.
Data types
All computers have at least two basic data types, characters and integers. While European
languages can get by with a character width of 8 bits, integers must be at least 16 bits wide to
be of any use, and most UNIX systems use 32 bit integers, as much storage as four characters.
Problems can obviously arise if you port a package to a system whose int size is less than the
author of the package expected.
Integer sizes
Data sizes arent the problem they used to betimes were when a machine word could be 8,
12, 16, 18, 24, 30, 32, 36, 48, 60, 64 or 72 bits long, and so were the primary integer data
objects. Nowadays you can expect nearly every machine to have an int of 16, 32 or 64 bits,
and the vast majority of these have a 32 bit int. Still, one of the biggest problems in ANSI C
is the lack of an exact definition of data sizes. int is the most used simple data type, but
depending on implementation it can vary between 16 and 64 bits long. short and long can be
the same size as int, or they can be shorter or longer, respectively. There are advantages to
this approach: the C compiler will normally choose an int which results in the fastest pro-
cessing time for the processor on which the program will run. This is not always the smallest
data size: most 32-bit machines handle 32 bit arithmetic operations faster than 16 bit opera-
tions. Problems dont arise until the choice of int is too small to hold the data that the pro-
gram tries to store in it. If this situation arises, you have a number of options:
You can go through the sources with an editor and replace all occurrences of the word
int with long (and possibly short with int).*
* If you do this, be sure to check that you dont replace short int with int int!
153
You can simplify this matter a little by inserting the following definition in a common
header file:
#define int long
This has the disadvantage that you cant define short as int, because preprocessor
macros are recursive, and you will end up with both int and short defined as long.
Some compilers, particularly those with 16-bit native ints, offer compiler flags to gener-
ate longer standard ints.
All these solutions have the problem that they do not affect library functions. If your sys-
tem library expects 16-bit integers, and you write
int x = 123456;
printf ("x is %d\n", x);
the library routine printf still assumes that the parameter x is 16 bits long, and prints out the
value as a signed 16-bit value (-7616), not what you want. To get it to work, you need to
either specify an alternate library, or change the format specification to printf:
int x = 123456;
printf ("x is %l\n", x);
There are a few other things to note about the size of an int:
Portable software doesnt usually rely on the size of an int. The software from the Free
Software Foundation is an exception: one of the explicit design goals is a 32-bit target
machine.
The only 64-bit machine that is currently of any significance is the DEC Alpha. You
dont need to expect too many problems there.
16 bit machinesincluding the 8086 architecture, which is still in use under MS-
DOS are a different matter, and you may experience significant pain porting, say, a
GNU program to MS-DOS. If you really want to do this, you should look at the way gcc
has been adapted to MS-DOS: it continues to run in 32-bit protected mode and has a
library wrapper* to allow it to run under MS-DOS.
the mantissa, then you should prepare for some serious re-writing.
Pointer size
For years, people assumed that pointers and ints were the same size. The lax syntax of early
C compilers didnt even raise an eyebrow when people assigned ints to pointers or vice-versa.
Nowadays, a number of machines have pointers that are not the same size as ints. If you are
using such a machine, you should pay particular attention to compiler warnings that ints are
assigned to pointers without a cast. For example, if you have 16-bit ints and 32-bit pointers,
sloppy pointer arithmetic can result in the loss of the high-order bits of the address, with obvi-
ous consequences.
Address space
All modern UNIX variants offer virtual memory, though the exact terminology varies. If you
read the documentation for System V.4, you will discover that it offers virtual memory,
whereas System V.3 only offered demand paging. This is more marketspeak than technology:
System V.2, System V.3, and System V.4 each have very different memory management, but
we can define virtual memory to mean any kind of addressing scheme where a process address
space can be larger than real memory (the hardware memory installed in the system). With
this definition, all versions of System V and all the other versions of UNIX you are likely to
come across have virtual memory.
Virtual memory makes you a lot less dependent on the actual size of physical memory. The
software from the Free Software Foundation makes liberal use of the fact: programs from the
GNU project make no attempt to economize on memory usage. Linking the gcc C++ com-
piler cc1plus with GNU ld uses about 23 MB of virtual address space on System V.3 on an
Intel architecture. This works with just about any memory configuration, as long as
Your processes are allowed as much address space as they need (if you run into trouble,
you should reconfigure your kernel for at least 32 MB maximum process address space,
more if the system allows it).
You have enough swap space.
You can wait for the virtual memory manager to do its thing.
From a configuration viewpoint, we have different worries:
Is the address space large enough for the program to run?
How long are pointers? A 16 bit pointer can address only 64 kilobytes, a 32 bit pointer
can address 4 GB.
How do we address memory? Machines with 16 bit pointers need some kind of addi-
tional hardware support to access more than 64 kilobytes. 32 bit pointers are adequate
for a flat addressing scheme, where the address contained in the pointer can address the
entire virtual address space.
Modern UNIX systems run on hardware with 32 bit pointers, even if some machines have ints
with only 16 bits, so you dont need to worry much about these problems. Operating systems
such MS-DOS, which runs on machines with 16 bit pointers, have significant problems as a
result, and porting 32 bit software to them can be an adventure. Well touch on these prob-
lems in Chapter 20, Compilers, page 346.
Character order
The biggest headache you are likely to encounter in the field of hardware dependencies is the
differing relationship between int and character strings from one architecture to the next.
Nowadays, all machines have integers large enough to hold more than one character. In the
old days, characters in memory werent directly addressable, and various tricks were
employed to access individual characters. The concept of byte addressing, introduced with
the IBM System/360, solved that problem, but introduced another: two different ways of look-
ing at bytes within a word arose. One camp decided to number the bytes in a register or a
machine word from left to right, the other from right to left. For hardware reasons, text was
always stored from low byte address to high byte address.
A couple of examples will make this more intelligible. As we saw above, text is always
stored low byte to high byte, so in any architecture, the text UNIX would be stored as
0 1 2 3
U N I X
Some architectures, such Sparc and Motorola 68000, number the bytes in a binary data word
from left to right. This arrangement is called big-endian. On a big-endian machine, the bytes
are numbered from left to right, so the number 0x12345678 would be stored like
0 1 2 3
12 34 56 78
Others, notably older Digital Equipment machines and all Intel machines, number the bytes
the other way round: byte 0 in a binary data word is on the right, byte 3 is on the left. This
arrangement is called little-endian.* The same example on a little-endian machine would look
like:
3 2 1 0
12 34 56 78
This may look just the same as before, but the byte numbers are now numbered from right to
left, so the text now reads:
* The names big-endian and little-endian are derived from Jonathan Swifts Gullivers Travels, where
they were a satirical reference to the conflicts between the Catholics and the Church of England in the
18th Century.
3 2 1 0
X I N U
As a result, this phenomenon is sometimes called the NUXI* syndrome. This is only one way
to look at it, of course: from a memory point of view, where the bytes are numbered left to
right, it looks like
0 1 2 3
78 56 34 12
and
0 1 2 3
U N I X
Its rather confusing to look at the number 0x12345678 as 78563412, so the NUXI (or XINU)
view predominates. Its easier to grasp the concepts if you remember that this is all a matter
of the mapping between bytes and words, and that text is always stored correctly from low
byte to high byte.
An alternative term for big-endian and little-endian is the term byte sex. To make matters
even more confusing, machines based on the MIPS chips are veritable hermaphroditesall
have configurable byte sex, and the newer machines can even run different processes with dif-
ferent byte sex.
The problem of byte sex may seem like a storm in a teacup, but it crops up in the most
unlikely situation. Consider the following code, originally written on a VAX, a little-endian
machine:
int c = 0;
On a little-endian machine, the single character is input to the low-order byte of the word, so
the comparison is correct, and entering the character q causes the program to stop. On a
32-bit big-endian machine, entering the character q sets c to the value 0x71000000, not the
same value as the character q. Any good or even mediocre compiler will of course warn you
if you hand the address of an int to read, but only if you remember to include the correct
header files: it happens anyway.
* Why not XINU? Because the term arose when words were 16 bits long. The PDP-11, for example,
stored ints (16 bit quantities) in a little-endian format, so pairs of bytes were swapped. The PDP-11
also had 32 bit long quantities that were stored with their component words in a big-endian format.
This arrangement has been called mixed-endian, just to add to the general confusion.
This discussion has concentrated on how characters are ordered within words, but the same
considerations also affect bit fields within a word. Most hardware platforms dont support bit
fields directly: theyre an idea in the mind of the compiler. Nonetheless, all architectures
define a bit order: some number from left to right, some from right to left. Well-written pro-
grams dont rely on the order of bit fields in ints, but occasionally you see register definitions
as bit fields. For example, the 4.4BSD sources for the HP300 include the following definition:
struct ac_restatdb
{
short ac_eaddr; /* element address */
u_int ac_res1:2,
ac_ie:1, /* import enabled (IEE only) */
ac_ee:1, /* export enabled (IEE only) */
ac_acc:1, /* accessible from MTE */
ac_exc:1, /* element in abnormal state */
ac_imp:1, /* 1 == user inserted medium (IEE only) */
ac_full:1; /* element contains media */
};
This definition defines individual bits in a hardware register. If the board in question fits in
machines that number the bits differently, then the code will need to be modified to suit.
Data alignment
Most architectures address memory at the byte level, but that doesnt mean that the underlying
hardware treats all bytes the same. In the interests of efficiency, the processor accesses mem-
ory several bytes at a time. A 32-bit machine, for example, normally accesses data 4 bytes at
a time this is one of the most frequent meanings of the term 32-bit machine. Its the com-
bined responsibility of the hardware and the software to make it look as if every byte is
accessed in the same way.
Conflicts can arise as soon as you access more than a byte at a time: if you access 2 bytes
starting in the last byte of a machine word, you are effectively asking the machine to fetch a
word from memory, throw away all of it except the last byte, then fetch another word, throw
away all except the first, and make a 16 bit value out of the two remaining bytes. This is obvi-
ously a lot more work than accessing 2 bytes at an even address. The hardware can hide a lot
of this overhead, but in most architectures there is no way to avoid the two memory accesses
if the address spans two bus words.
Hardware designers have followed various philosophies in addressing data alignment. Some
machines, such as the Intel 486, allow unaligned access, but performance is reduced. Others,
typically RISC machines, were designed to consider this to be a Bad Thing and dont even try:
if you attempt to access unaligned data, the processor generates a trap. Its then up to the soft-
ware to decide whether to signal a bus error or simulate the transferin either case its unde-
sirable.
Compilers know about alignment problems and solve them by moving data to the next
address that matches the machines data access restrictions, leaving empty space, so-called
padding in between. Since the C language doesnt have any provision for specifying
alignment information, youre usually stuck with the solution supplied by the compiler writer:
the compiler automatically aligns data of specific types to certain boundaries. This doesnt do
much harm with scalars, but can be a real pain with structs when you transfer them to disk.
Consider the following program excerpt:
struct emmental
{
char flag;
int count;
short choice;
int date;
short weekday;
double amount;
}
emmental;
read_disk (struct emmental *rec)
{
if (read (disk, rec, sizeof (rec)) < sizeof (rec))
report_bad_error (disk);
}
On just about any system, emmental looks like a Swiss cheese: on an i386 architecture,
shorts need to be on a 2-byte boundary and ints and doubles need to be on a 4-byte boundary.
This information allows us to put in the offsets:
struct emmental
{
char flag; /* offset 0 */
/* 3 bytes empty space */
int count; /* offset 4 */
short choice; /* offset 8 */
/* 2 bytes empty space */
int date; /* offset 12 */
short weekday; /* offset 16 */
/* 2 bytes empty space */
double amount; /* offset 20 */
}
emmental;
A lot of the software you get looks as if it has never seen a good programmer.
Apart from the waste of space, alignment brings a host of other problems. If the first three
fields really are a database key, somebody (probably the database manager) has to ensure that
the gaps are set to a known value. If this database is shared between different machines, our
read_disk routine is going to be in trouble. If you write the record on an i386, it is 28 bytes
long. If you try to read it in on a Sparc, read_disk expects 32 bytes and fails. Even if you
fix that, amount is in the wrong place.
A further problem in this example is that Sparcs are big-endian and i386s are little-endian:
after reading the record, you dont just need to compact it, you also need to flip the bytes in
the shorts, ints and doubles.
Good portable software has accounted for these problems, of course. On the other hand, if
your program compiles just fine and then falls flat on its face when you try to run it, this is one
of the first things to check.
Instruction alignment
The part of the processor that performs memory access usually doesnt distinguish between
fetching instructions from memory and fetching data from memory: the only difference is
what happens to the information after it has reached the CPU. As a result, instruction align-
ment is be subject to the same considerations as data alignment. Some CPUs require all
instructions to be on a 32 bit boundarythis is typically the case for RISC CPUs, and it
implies that all instructions should be the same lengthand other CPUs allow instructions to
start at any address, which is virtually a requirement for machines with variable length
instructions.* As with data access, being allowed to make this kind of access doesnt make it a
good idea. For example, the Intel 486 and Pentium processors execute instructions aligned on
any address, but they run significantly faster if the target address of a jump instruction is
aligned at the beginning of a processor word the alignment of other instructions is not
important. Many compilers take a flag to tell them to align instructions for the i486.
* Some machines with variable length instructions do have a requirement that an instruction fit in a sin-
gle machine word. This was the case with the Control Data 6600 and successors, which had a 60 bit
word and 15 or 30 bit instructions. If a 30 bit instruction would have started at the 45 bit position inside
a word, it had to be moved to the next word, and the last 15 bits of the previous instruction word were
filled with a nop, a no-operation instruction.
* 4.3BSD was released in 1987, 4.4BSD in 1994. In the time in between, releases had names like
4.3BSD Tahoe, 4.3BSD Reno, and NET/2. For want of a better term, Stevens refers to systems roughly
corresponding to NET/2 as 4.3+BSD.
161
Interprocess communication
interprocess communication (frequently written as the abbreviation IPC), the ability to trans-
fer data between processes, was one of the important original concepts of UNIX. The original
methods were what you might expect of a concept that, at the time, was revolutionary and still
under development: there were more than a few limitations. Even today there is no agreement
on how interprocess communication should take place.
In this section well look very briefly at the various kinds of interprocess communication, and
what to do if the package you are porting uses a method your kernel doesnt support. To start
with the bad news: if you find your kernel doesnt support the IPC model that the package
expects, you will probably need to make significant modifications to adapt it to a different
model.
Interprocess communication was originally limited to a single processor, but of course net-
work communication is also a form of interprocess communication. Well touch briefly on
network communication in the following discussion.
UNIX systems offer a bewildering number of different forms of interprocess communication:
Pipes are the original form of communication and are found in all versions of UNIX.
They have the disadvantages that they transfer data in one direction only, and that they
can only connect two processes that have a common ancestor.
Sockets are the BSD interprocess communication mechanism: they are by far the most
powerful mechanism, offering unidirectional, bidirectional and network communication.
In BSD systems, they are even used to implement the pipe system call.
STREAMS* is a generalized I/O concept available in newer System V systems and their
derivatives. It was originally intended to replace character device drivers, but its flexibil-
ity makes it useful for interprocess communication as well. Like sockets, it can be used
both for local and remote communication. UNIX Network Programming, by Richard
Stevens, describes STREAMS in some detail, and The Magic Garden Explained
describes the implementation. We wont consider them further here.
Stream pipes differ from normal pipes by being able to transfer data in both directions.
They have no particular connection with STREAMS.
FIFOs, also called named pipes, are like pipes, but they have a name in the file system
hierarchy.
Named stream pipes are stream pipes with names. They bear the same relationship to
stream pipes that FIFOs do to normal pipes.
System V IPC is a bundle that offers message queues, yet another form of message pass-
ing, shared memory, which enables processes to pass data directly, and semaphores,
which synchronize processes.
* Why the shouting? STREAMS was derived from the Eighth Edition Streams concept (see S Stream
Input-Output System, by Dennis Ritchie). System V always spells it in upper case, so this is a con-
venient way of distinguishing between the implementations.
In the following sections, well look at these features in a little more detail.
Pipes
The original UNIX interprocess communication facility was pipes. Pipes are created by the
pipe function call:
#include <unistd.h>
This call creates a pipe with two file descriptors, a read descriptor and a write descriptor. It
returns the value of the read descriptor to fildes [0] and the value of the write descriptor to
fildes [1]. At this point, only the creating process can use the pipe, which is not very use-
ful. After calling fork, however, both of the resultant processes can use the pipe. Depending
on their purpose, the processes may decide to close one direction of the pipe: for example, if
you write output to the more program, you dont expect any reply from more, so you can close
the read file descriptor.
A fair amount of code is involved in opening a pipe, starting a new process with fork and
exec and possibly waiting for it terminate with wait. The standard library functions popen
and pclose make this job easier:
#include <stdio.h>
popen creates a pipe, then forks and execs a shell with command as its parameter. type speci-
fies whether the pipe should be open for reading (r) or writing (w). Since pipes are unidi-
rectional, they cannot be opened both for reading and for writing.
After opening the command, you can write to the process with normal write commands. On
completion, pclose waits for the child process to terminate and closes the file descriptors.
Sockets
Sockets were originally developed at Berkeley as part of the TCP/IP networking implementa-
tion introduced with 4.2BSD, but they are in fact a general interprocess communication facil-
ity. In BSD systems, the other interprocess communication facilities are based on sockets.
Most of the features of sockets are related to networking, which we dont discuss here. The
call is:
#include <sys/types.h>
#include <sys/socket.h>
domain specifies the communications domain. Common domains are AF_UNIX (UNIX
domain),* used for local communication, AF_INET (Internet domain), and AF_ISO (ISO
protocol domain).
type specifies the type of socket. For local interprocess communication, you would use
SOCK_STREAM, which supplies a reliable two-way communication channel.
protocol specifies the communications protocol to use. In the UNIX domain, this
parameter is not used and should be set to 0.
As we shall see in the next section, the way that pipes are implemented means that you need
two sockets to simulate a pipe. You can do this with the socketpair system call, which creates
a pair of file descriptors with identical properties.
#include <sys/types.h>
#include <sys/socket.h>
int socketpair (int domain, int type, int protocol, int *sv);
Currently, socketpair works only in the UNIX domain, so you dont have much choice in
the parameters: domain must be AF_UNIX, type must be SOCK_STREAM, and protocol is
meaningless in the UNIX domain. The only important parameter is sv, which is where the
socket descriptors are returnedexactly the same thing as the fildes parameter to pipe.
Most systems have some kind of socket support, but sometimes it is just an emulation library
that omits significant functionality, such as the UNIX domain and the socketpair call.
Many older System V sockets emulation libraries also have a bad reputation regarding perfor-
mance and reliability. On the other hand, many System V.3 ports included the original Berke-
ley socket implementation in the kernel.
* Not all UNIX implementations support UNIX domain sockets. In particular, some System V systems
support only the Internet domain. People with a System V background often place the emphasis on the
word domain, and some even refer to UNIX domain sockets as domain sockets. As you can see
from the above, this is incorrect.
Stream pipes
Most systems allow you to create bidirectional pipes. For some reason, theyre generally
called stream pipes, which is not a good name at all.
In System V.4, regular pipes are bi-directional, so you dont need to do anything special.
In 4.4BSD, the socketpair system call, which we have already seen, creates stream pipes,
so youd expect regular pipes to be bidirectional in 4.4BSD as well. In fact, before
returning, the library function pipe closes one descriptor in each direction, so 4.4BSD
pipes really are unidirectional. If you want a stream pipe, just use the socketpair sys-
tem call.
In System V.3 systems with STREAMS, bidirectional pipes are possible too, but things
are more difficult: you have to connect two streams back to back. See UNIX Network
Programming for a discussion of how to do this.
FIFOs
FIFOs are pipes with file names, which allow unrelated processes to communicate with each
other. To create a FIFO, you use the function mkfifo:
#include <sys/stat.h>
This call corresponds exactly to mkdir, except that it creates a FIFO instead of a directory.
BSD implements mkfifo as a system call, while System V.4 implements it as a library func-
tion that calls mknod to do the work. System V.3 systems frequently implemented it as a sys-
tem call. Once you have created a FIFO, you can use it just like a file: typically, one process,
the listener process, opens the FIFO for reading, and one or more open it for writing to the lis-
tener process.
System V IPC
System V supplies an alternative form of interprocess communication consisting of three fea-
tures: shared memory, message queues and semaphores. SunOS 4 also supports System V
IPC, but pure BSD systems do not. In the industry there is a significant amount of aversion to
this implementation, which is sometimes called The Three Ugly Sisters.
System V IPC is overly complicated and sensitive to programming bugs, which are two of the
main reasons why it has not been implemented on other systems. Converting programs writ-
ten for System V IPC to other methods of interprocess communication is non-trivial. If you
have a BSD system with kernel sources, it might be easier to implement Daniel Boulets free
software implementation (see Appendix E, Where to get sources).
Shared memory
An alternative form of interprocess communication involves sharing data between processes.
Instead of sending a message, you just write it into a buffer that is also mapped into the
address space of the other process. There are two forms of shared memory that you may
encounter on UNIX systemsSystem V shared memory and mmap, which is more commonly
used for mapping files to memory. Well look at mmap in Chapter 14, File systems, page 232.
System V shared memory is implemented with four system calls:
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
shmget allocates a shared memory segment or adds the process to the list of processes
sharing the segment. The shared memory segment identifier is conceptually like a file
name or an identifier, but for some reason they are called keys when talking about Sys-
tem V shared memory. It returns a segment identifier, conceptually like a file number.
shmctl performs control operations on shared memory segments. It can set ownerships
and permissions, retrieve status information, or remove shared memory segments. Like
files, shared memory segments remain on the system until explicitly removed, even if
they are currently not assigned to any process.
shmat attaches the shared memory segment shmid to the calling process.
shmdt detaches a shared memory segment from the calling process.
With some limitations, you can use mmap to replace System V shared memory. The limita-
tions are that mmap on non-System V platforms normally maintains separate data pages for
each process, so if you write to a page in one process, other processes will not see the new
data. You need to call msync in order to update the segments used by other processes.
Between the time when you modify the segment and when you call msync, the data is incon-
sistent. msync is not a fast call, so this could also cripple performance.
Message queues
As if there werent enough ways of passing data between processes already, System V IPC
includes message queues. Message queues are rather like FIFOs, but there are two differ-
ences:
A FIFO transmits a byte stream, but a message queue is record oriented.
Messages can have different priorities, which determine the sequence in which they are
received, if the receiving process allows them to queue up.
The system calls to handle message queues are analogous to the shared memory calls:
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
Semaphores
One disadvantage with shared memory implementations is that one process doesnt know
when another process has done something. This can have a number of consequences:
Two processes may modify the same area at the same time.
One process may be waiting for the other process to do something, and needs to know
when it has finished.
There are two possible solutions: send a signal, or use semaphores.
A semaphore is a means of voluntary process synchronization, similar to advisory locking. To
use the facility, a process requests access to the semaphore. If access is currently not possible,
the process blocks until access is permitted. Unlike locking, semaphores allow more than one
process access to the semaphore at any one point. They do this by maintaining a counter, a
small positive integer, in the semaphore. When a process accesses the semaphore, it decre-
ments the counter. If the value of the counter is still non-negative, the process has access, oth-
erwise it is blocked. This could be used to gain access to a limited number of resources.
System V semaphores look superficially similar to System V shared memory. There are three
functions:
int semctl (int semid, int semnum, int cmd, ... /* union semun arg */);
int semget (key_t key, int nsems, int semflg);
int semop (int semid, struct sembuf *sops, size_t nsops);
The implementation is less than perfect. In particular, it is overly complex, and it almost
encourages deadlocks, situations where no process can continue:
Instead of a single counter, a System V semaphore declares an array of counters. The
size of the array is determined by the nsems parameter of the semget system call.
It takes two calls (semget and semctl) to create and initialize a semaphore. Theoreti-
cally, this creates an opportunity for another process to come and initialize the sema-
phore differently.
Its possible for semaphores to remain locked after a process ends, which means that a
reboot is necessary to unlock the semaphore again. A flag is provided to specify that a
semaphore should be removed on exit, but you cant rely upon it completely.
The implementation is not very fast.
exec
exec is one of the original system calls at the heart of the UNIX system, so it may come as a
surprise to discover that exec is no longer a system call on modern systemsinstead, it is
implemented as a library function in terms of new system calls such as execve. Even the
Seventh Edition man pages stated
Nowadays, there are a large number of alternatives. Your system probably has most of the
following calls:
#include <unistd.h>
extern char **environ;
All these functions do exactly the same thing: they replace the process image with a process
image from the absolute executable whose file name is specified in the first argument (path or
file). They differ only in the manner in which they supply the parameters:
The parameter path specifies an absolute pathname. If this file does not exist, the call
fails.
Alternatively, the parameter file specifies a file to be searched via the PATH environ-
ment variable, the way the shell does when a file name is specified.
The parameter argv is a pointer to a NULL terminated list of parameters.
Alternatively, you can place the arguments, including the terminating NULL, in the call as
a series of args.
If the parameter envp is specified, it is a pointer to a NULL-terminated list of environment
variables. This is typically used when the child process should be given a different envi-
ronment from the parent process.
If envp is not specified, the environment variables are taken from the parents environ-
ment (via the global pointer environ).
One further function deserves mention: exect, which is supplied only in newer BSD systems,
takes the same parameters as execve, but enables program tracing facilities.
The total storage available for the argument list and the enviroment varies from system to sys-
tem. System V traditionally has only 5120 characters. POSIX.1 requires at least 20480 char-
acters, and this is the standard value for newer BSD systems. Many Makefiles take advantage
of these large parameter lists, and frequently a package fails to build under System V because
the parameter lists are too long: you get the message
make: execve: /bin/sh: Arg list too long
We looked at what we can do to solve these problems in Chapter 5, Building the package,
page 74.
{
int rlim_cur; /* current (soft) limit */
int rlim_max; /* hard limit */
};
The rlimit structure defines two values for each resource, the current value and the maxi-
mum value. getrlimit returns this information, setrlimit sets a new current value. Table
12-1 shows which limits can be set:
If your system doesnt have these functions, theres not much you can do except guess. In
some cases, header files will contain similar information declared as constants, but its not a
very satisfactory alternative.
Process groups
Where other operating systems use a single program to perform an operation, UNIX fre-
quently uses a group of cooperating processes. Its useful to be able to define such a group,
particularly when they access terminals. Advanced Programming in the UNIX environment,
by Richard Stevens, describes all you will want to know about process groups. Here, well
look at some minor differences in implementations.
setpgid
setpgid adds a process to a process group:
#include <unistd.h>
pid is the process ID of the process that is to be added to the process group, and pgrp is the
process group to which it should be added. It returns 0 on success and -1 with an error code
in errno on failure.
Normally you will see setpgid used to add the calling process to a group; this can be done
by setting pid to 0. System V versions also allow pgrp to be 0: this specifies that the process
id should be the same as pid, and that this process will become a process group leader.
setpgrp
setpgrp is obsolescent. There are two different implementations, both of which duplicate
functionality supplied by other functions:
In more modern BSD systems, it is the same thing as setpgid:
int setpgrp (pid_t pid, pid_t pgrp); BSD versions
In System V, it creates a new process group with the calling process as group leader, and
adds the calling process to the group. It also releases the controlling terminal of the call-
ing process. This is the same thing as setsid:
int setpgrp (); System V versions
If you run into trouble with this function, its best to replace it with setpgid or setsid,
depending on the functionality that was intended.
setsid
setsid creates a new process group with the calling process as group leader, and adds the
calling process to the group. It also releases the calling process from its controlling terminal:
#include <unistd.h>
setuid
setuid changes the effective user ID. If your current effective user ID is root, you can set it
to any valid user ID. There, unfortunately, the similarity ends:
In systems without a saved set user ID, including SunOS 4 and System V.3, setuid sets
the effective user ID and the real user ID if the current effective user ID is root, otherwise
it sets only the effective user ID. The function call succeeds if the argument to setuid is
the real user ID or the effective user ID, or if the effective user ID is root. Once you have
changed away from the old effective user ID and root, there is no way to change back.
On System V systems with saved set user ID, setuid sets the effective user ID and the
real user ID if the current effective user ID is root, otherwise it sets only the effective
user ID. It does not change the saved set user ID. The function call succeeds if the argu-
ment to setuid is the real user ID, the effective user ID, or the saved set user ID, or if
the effective user ID is root. This means that you can switch back and forth between the
ID of the program owner and the ID of the process which started it.
On BSD systems with saved set user ID, setuid sets the real, effective, and saved set
user IDs. The function call succeeds if the argument to setuid is the real user ID, or if
the effective user ID is root. Unlike System V.4, non-root users cannot use setuid to set
the user ID to the saved set user ID. The saved set user ID is of no use to BSD
setuidinstead, BSD systems use seteuid, which sets only the effective user ID to
either the real user ID or the saved set user ID.
setreuid
BSD versions since 4.2BSD have the system call setreuid, which takes two parameters:
int setreuid (int ruid, int euid);
You can use it to swap the effective and real user IDs, so you dont really need a saved set user
ID. For non-privileged users, ruid and euid can be either the current real user ID or the cur-
rent effective user ID, or -1 to indicate no change. This function was needed in BSD up to
and including 4.3BSD, since these versions did not support the concept of a saved set user ID.
On non-BSD systems only, you can replace this function with setuid if your system supports
saved set user IDs.
seteuid
As we noted above, BSD setuid cannot change to the saved set user ID. The BSD solution
to this problem, which has been proposed for adoption in a new revision of POSIX.1, is the
function seteuid. It sets the effective user ID to euid if euid corresponds either to the real
user ID or the saved set user ID. Unlike setuid, it sets only the effective user ID.
setruid
In addition to seteuid, BSD systems provide the call setruid, which sets the real user ID to
the effective or real user ID. setruid is considered non-portable. Future BSD releases plan
to drop it.
On System V.4 systems with _POSIX_SAVED_IDS, use setuid (ssuid), where ssuid
is the saved set user ID. You can get the value of ssuid by calling geteuid before
changing the initial effective user ID, since theyre the same at program start.
On BSD systems which support saved set user IDs, use seteuid (ssuid). As with
System V.4, you can get the value of ssuid by calling geteuid before changing the ini-
tial effective user ID.
vfork
vfork was introduced in 3BSD as a more efficient version of fork: in those days, fork
copied each data area page of the parent process for the child process, which could take a con-
siderable time. Typically, the first thing a child does is to call exec to run a new program,
which discards the data pages, so this was effectively wasted time. vfork modified this be-
haviour so that the pages were shared and not copied.
This is inherently very dangerous: very frequently the parent waits until the child has done
something before continuing. During this time, the child can modify the parents data, since it
is shared. More modern techniques, such as copy on write*, have eliminated the need for this
function. You should be able to replace it with fork (the semantics are identical). Unfortu-
nately, some obscene programs rely on the fact that they can manipulate the parents data
* With copy on write, the data pages are set to be write-protected. The first write causes an interrupt,
effectively a bus error, which the system intercepts. The system makes a copy of the single page and
resets write protection for both the original and the copy, allowing the write to proceed.
Unfortunately, various flavours define the value of the status return differently. This is a cos-
metic difference, not a real difference: the status information consists of a number of bit fields
that depend on the kind of status:
The low-order 7 bits contain the number of the signal that terminated the process, or 0 if
the process called exit.
The bit 0x80 is set if a core dump was taken.
The next 8 bits are the return code if the process called exit.
If the process is stopped (if it can be restarted), the low-order 8 bits are set to 127 (0x7f), and
the next byte contains the number of the signal that stopped the process.
This information is the same on all versions of UNIX, but there is no agreement on how to
represent this information. Older BSD systems defined a union to represent it:
union __wait
{
int w_status; /* status as int */
struct
{
unsigned short w_Termsig:7; /* termination signal */
unsigned short w_Coredump:1; /* core dump indicator */
unsigned short w_Retcode:8; /* exit code if w_termsig==0 */
}
w_T;
struct
{
unsigned short w_Stopval:8; /* == W_STOPPED if stopped */
unsigned short w_Stopsig:8; /* signal that stopped us */
}
w_S;
};
WTERMSIG (status) evaluates to the number of the signal that caused the termina-
tion of the process.
WCOREDUMP (status) is true if a core dump was created.
WIFSTOPPED (status) is true if the process is stopped and can be restarted. This
macro can be true only if the waitpid call specified the WUNTRACED option or if the
child process is being traced. If this is true, WSTOPSIG (status) returns the number of
the signal that caused the process to stop.
Some systems offer both of these options, sometimes incompletely. For example, SunOS 4
defines w_Coredump in the union __wait, but does not define the corresponding WCORE-
DUMP macro.
These varying differences cause problems out of all proportion to the importance of the infor-
mation contained. In particular, the newer macros do not allow you to change the status, you
can only read it. Some programs, for example BSD make, modify the status. This makes it
difficult to port it to System V or another system which does not understand union wait.
waitpid
waitpid is a variant of wait that waits for a specific process to terminate. It is part of all
modern UNIX implementations:
#include <sys/wait.h>
waitpid waits for process pid to terminate. Its behaviour is governed by a number of bit-
mapped options:
Set WNOHANG to specify to return immediately, even if no status is available. If the status
is not available, the functions return the process number 0. Not all systems support this
behaviour.
Specify WUNTRACED if you want the status of stopped processes as well as processes that
have terminated. Some systems do not return complete status information for stopped
processes.
Under System V.4, use WCONTINUED to report the status of any process that has contin-
ued (in other words, one that is no longer stopped) since the last status report.
Also under System V.4 you can set the option WNOWAIT to specify that the process should
not terminate (it remains a zombie). This means that you can call waitpid again and get
the same information.
The value of status is the same as with waitsee the previous section for further details.
If you run into problems with waitpid, it may be a bug: some versions of System V.3,
including most current versions of SCO UNIX, return a process ID if a process is waiting, and
an error number such as ECHILD (10) if nothing is waiting, so if your freshly ported program
keeps reporting the demise of process 10, this could be the problem. Its almost impossible to
work around this bug about the only thing you can do is to use some other system call.
Not all implementations return usage information to rusage when the process is stopped (and
not terminated). The definition of struct rusage is implementation-dependent and defined
in sys/resource.h. See the file sys/sys/resource.h in the 4.4BSD Lite distribution for further
details.
179
signal handler for every conceivable signal, so the kernel supplies two default methods of han-
dling the signal. The choice of a signal handler or one of the two defaults is called the dispo-
sition of the signal. Initially, each signals disposition is set either to ignore the signal or to
terminate the process if the signal occurs. In some cases, the system writes a core file, a copy
of the state of the process, when the process is terminated.
Signals may come from a number of different sources:
External events. For example, pressing CTRL-C or DEL on most systems causes the ter-
minal driver to send a SIGINT signal to the foreground process group of the terminal.
Internal events. For example, alarm causes a SIGALRM signal after the specified time-
out.
Hardware interrupts. For example, if a process attempts to access a page that is not part
of its address space, it will receive a SIGSEGV or SIGBUS signal.
As the result of another process calling kill.
In this chapter, well consider which signals are supported by which operating systems, and
how signals are implemented in different operating systems.
Supported signals
The Seventh Edition had 15 signals, and current implementations allow up to 31, though not
all are used. In the course of time, the meanings have also diverged somewhat. Table 13-1
gives an overview of which signals are present in which implementations.
means that you can use one signal handler to handle multiple signalsbut if the same signal
reoccurs before the signal handler has finished handling the previous instance, it could happen
again and again, and the result can be a stack overflow with repeated signal handler calls.
The original signal implementation, which we call unreliable signals, had a simplistic attitude
to this problem: it reset the signal dispostion to the default, which meant that if another signal
occurred while the previous one was being processed, the system would either ignore the sig-
nal (so it would lose the signal) or terminate the process (which is probably not what you
want). It was up to the signal handler to reinstate the signal disposition, and this couldnt be
done immediately without running the risk of stack overflow.
All newer signal implementations provide so-called reliable signals. The signal disposition is
not changed on entry to the signal handler, but a new signal will not be delivered until the sig-
nal handler returns. This concept is called blocking the signal: the system notes that the signal
is pending, but doesnt deliver it until it is unblocked.
There are a number of things that the term reliable signal does not mean:
It doesnt imply that the underlying kernel implementation is bug-free. Depending on
the implementation, there is still a slight chance that the kernel will lose the signal.
It doesnt imply that a signal cannot get lost. The method used to queue signals is to set
a bit in a bit mask. If multiple signals of the same kind occur while the signal is blocked,
only one will be delivered.
It doesnt imply that you dont need reentrant signal handlers. The system blocks only
the signal that is currently being handled. If you have a single handler for multiple sig-
nals, it will need to be reentrant. In particular, this means that you should at least be very
careful with static variables and preferably use few local variables (since they take up
stack space). You should also be careful with the functions you callwell take another
look at this on page 187.
The semantics of each implementation differ in subtle ways, so changing to a different set of
signal calls involves more than just changing the function calls and parameters. Heres a brief
overview of the differences you might encounter:
With unreliable signals, after a signal occurs, the signal disposition is reset to default, so
the signal handler must reinstate itself before returning. If a second signal occurs before
the disposition is reinstated, the process may be terminated (if the default disposition is
terminate) or the signal may be completely forgotten (if the default disposition is ignore).
The names and purposes of the signals differ significantly from one implementation to
the next. See Table 13-2 for an overview.
In modern implementations, the function call signal varies in its meaning. In System
V, it uses the old, unreliable Seventh Edition signal semantics, while in BSD it is an
interface to the sigaction system call, which provides reliable signals. If youre port-
ing BSD signal to System V, you should modify the code use sigaction instead.
can be entered again before it has returned. This places a number of restrictions on the function. In par-
ticular, it cannot rely on external values, and may not use static storage.
The first parameter to a signal handler is always the number of the signal. Both System
V.4 and BSD can supply additional parameters to the signal handlers. Well look at the
additional parameters in more detail on page 183.
The handling of interrupted system calls varies from one system to the next. Well look
into this topic in more detail on page 186.
The difference between the signals SIGBUS and SIGSEGV is purely historical: it relates to
the PDP-11 hardware interrupt that detected the problem. In modern systems, it depends
on the whim of the implementor when you get which signal. POSIX.1 defines only
SIGSEGV, but this doesnt help much if the processor generates SIGBUS anyway. Its best
to treat them as being equivalent.
SIGCLD is a System V version of SIGCHLD. A number of hairy problems can arise with
SIGCLD; well look at them in more detail on page 186.
SIGILL was generated by the abort function in early BSD implementations. Early Sys-
tem V used SIGIOT instead. All modern implementations generate SIGABRT. Fre-
quently youll find that these two signals are in fact defined to have the same number; if
you run into troubles where one or the other is undefined, you could possibly do just this:
#define SIGIOT SIGABRT
Signal handlers
Modern versions of UNIX define signal handlers to be of type
void (*signal (int signum, void (*handler))) (int hsignum)
This is probably one of the most confusing definitions you are likely to come across. To
understand it, it helps to remember that we are talking about two functions:
The signal handler, called handler in this declaration, takes an int parameter hsignum
and returns a void pointer to the old signal handler function, which is of the same type
as itself.
The function signal, which takes two parameters. The first is signum, the number of
the signal to be handled, of type int, and the second is a pointer to a signal handler func-
tion handler. It also returns a void pointer to a signal handler function.
In fact, in many implementations the signal handler function takes additional parameters, and
you may find that your program takes advantage of them. Well look at these in the following
sections.
ucontext is defined in /usr/include/ucontext.h and contains information about the user con-
text at the time of the signal application. It includes the following fields:
uc_sigmask is the blocked signal mask.
us_stack points to the top of stack at the time the signal was delivered.
uc_mcontext contains the processor registers and any implementation specific context
data.
For example, assume you had set the signal handler for SIGFPE with the call in Example 13-1.
Example 131:
void bombout_handler (int signum,
struct siginfo *info,
struct ucontext *context);
sigset_t bombout_mask;
struct sigaction bad_error = {&bombout_handler, handler for the signal
&bombout_mask, signals to mask
SA_SIGINFO}; we want additional info
On receipt of a SIGFPE,
signal will be set to SIGFPE.
info->si_signo will also be set to SIGFPE.
On an i386 machine, info->si_code might be, for example, FPE_INTDIV (indicating
an integer divide by zero) or FPE_FLTUND (indicating floating point underflow).
The value of info->si_errno cant be relied on to have any particular value.
context->uc_sigmask contains the current signal mask.
context->uc_stack will point to the stack in use at the time the signal was delivered.
context->uc_mcontext will contain the contents of the processor registers at the time
of the interrupt. This can be useful for debugging.
code gives additional information about certain signalsyou can find this information in the
header file /usr/include/machine/trap.h. This file also contains information about how hard-
ware interrupts are mapped to signals. context is hardware-dependent context information
that can be used to restore process state under some circumstances. For example, for a Sparc
architecture it is defined as
struct sigcontext
{
int sc_onstack; /* sigstack state to restore */
int sc_mask; /* signal mask to restore */
/* begin machine dependent portion */
int sc_sp; /* %sp to restore */
int sc_pc; /* pc to restore */
int sc_npc; /* npc to restore */
int sc_psr; /* psr to restore */
int sc_g1; /* %g1 to restore */
int sc_o0; /* %o0 to restore */
};
The program of Example 13-1 wont compile under BSD, since BSD doesnt define SA_SIG-
INFO, and the parameters for bombout_handler are different. We need to modify it a little:
void bombout_handler (int signum,
int code,
struct sigcontext *context);
sigset_t bombout_mask;
struct sigaction bad_error = {&bombout_handler, handler for the signal
&bombout_mask, signals to mask
0};
... the rest stays the same
If you enter this signal handler because of a SIGFPE, you might find:
signum will be set to SIGFPE.
On an i386 machine, code might be, for example, FPE_INTOVF_TRAP (indicating an
integer divide by zero) or FPE_FLTUND_TRAP (indicating floating point underflow).
The value of sc_onstack would be the previous sigstack state.
context->sc_mask contains the current blocked signal mask, like context->uc_sig-
mask in the System V.4 example.
The rest of the context structure shows the same kind of register information that Sys-
tem V.4 stores in context->uc_mcontext.
In the Seventh Edition, if a system call was interrupted, it returned an error, and errno was
sent to EINTR. It was up to the process to decide whether to repeat the call or not. This added
a significant coding overhead to just about every program; the result was that programs usu-
ally did not provide for interrupted system calls, and died when it happened.
Later signal implementations improved on this state of affairs:
In 4.2BSD, signals automatically restarted the system calls ioctl, read, readv, wait,
waitpid, write and writev.
In 4.3BSD, the 4.2BSD signal implementation was modified so that the user could elect
not to restart specific system calls after interruption. The default remained to restart the
system call.
In POSIX.1, when you call sigaction you can specify that system calls interrupted by
specific signals should be restarted. This is done with the SA_RESTART flag in the field
sa_flags. If this flag is not set, the calls will not be restarted.
SunOS 4 does not have SA_RESTART, but it has SA_INTERRUPT instead, which is effec-
tively the reverse of SA_RESTART: system calls will be restarted unless SA_INTERRUPT is
set,
On modern systems, the action taken depends on the system calls you have used and the sys-
tem you are using:
With System V, you have the choice of no restart (unreliable signal or System V
sigset and friends) or POSIX.1 selective restart based on the signal (SA_RESTART with
sigaction).
With BSD, you have the choice of no restart (reliable signal based on sigaction),
default restart based on system calls (sigvec and friends) or again the POSIX.1 selective
restart based on the signal (SA_RESTART with sigaction).
Signal sets
A central difference between the Seventh Edition and System V implementations, on the one
side, and the BSD and POSIX.1 implementations, on the other side, is the way signals can be
specified. The Seventh Edition functions treat individual signals, which are specified by their
number. The BSD routines introduced the concept of the signal set, a bit map of type sigset_t,
that specifies any number of signals, as illustrated in Figure 13-1:
31 30 29 11 10 9 1 0
1 1 0 ... 0 0 0 ... 1
For each signal, if the corresponding bit in the bit map is set, the signal is said to be included
in the set. In this example, the signals specified are SIGUSR2, SIGUSR1 and SIGHUP. This
method enables any number of signals to be specified as the parameter of one call.
The kernel maintains two special signal sets for each process: the signal mask and the pending
signal set. The signal mask specifies which signals should currently not be delivered to the
process these signals are said to be blocked. This does not mean that they will be ignored:
if a signal occurs while it is blocked, the kernel notes that it has occurred and sets its bit in the
pending signal set. When a subesequent call to sigsetmask resets the bit for this signal in
the signal mask, the kernel delivers the signal to the process and clears the bit in the pending
signal set.
sigsetmask
sigsetmask sets the process signal mask:
#include <sys/signal.h>
int sigsetmask (int mask);
sigsetmask can be defined in terms of the POSIX.1 function sigprocmask using the
SIG_SETMASK flag see page 194 for more details.
sigblock
sigblock modifies the process signal mask. Unlike sigsetmask, it performs a logical OR
of the specified mask with the current signal mask, so it can only block signals and not enable
them.
#include <sys/signal.h>
int sigblock (int mask);
sigblock can be defined in terms of the POSIX.1 function sigprocmask using the
SIG_BLOCK flag see page 194 for more details.
sigvec
sigvec corresponds to the Seventh Edition signal: it sets the disposition of a signal. In addi-
tion, it can block other signals during the processing of a signal.
#include <signal.h>
... in signal.h is the definition
struct sigvec
{
void (*sv_handler) ();
sigset_t sv_mask;
int sv_flags;
};
signum is the signal whose disposition is to be changed. vec specifies the new disposition of
the signal, and the function returns the old disposition to ovec.
If vec->sv_mask is non-zero, it specifies the signals to block while the signal handler is run-
ning. This is logically ored with the current signal mask, so it works like an implicit sig-
block on entering the signal handler. On exit from the signal handler, the kernel reinstates
the previous signal mask.
flags can consist of:
SV_ONSTACK specifies to take the signal on alternate signal stack, if one has been
defined.
SV_INTERRUPT specifies that system calls should not be restarted after the signal handler
has completed.
sigvec is almost identical to the POSIX.1 function sigaction described on page
193 only the names of the sigvec structure and its members are different. Note, however,
that the flag SV_INTERRUPT has the opposite meaning from the POSIX.1 flag SA_RESTART,
which frequently has the same numeric value.
sigpause
sigpause combines the functionality of sigmask and pause: it first sets the signal mask and
then calls pause to wait for a signal to occur.
#include <sys/signal.h>
int sigpause (sigset_t sigmask);
Occasionally a process will use sigpause, usually to wait for I/O. In Example 13-3, it blocks
the signals SIGINT and SIGQUIT:
Example 133:
sigpause ((1 << SIGINT) | (1 << SIGQUIT)); /* wait for a signal */
sigset
sigset is the System V reliable equivalent of signal:
#include <signal.h>
void (*sigset (int sig, void (*disp) (int))) (int);
Unlike signal, the signal is not disabled when the signal handler is executing instead it is
blocked until the signal handler terminates.
sighold
sighold blocks the delivery of signal sig by setting the corresponding bit in the process sig-
nal mask. Semantically this corresponds to the POSIX.1 function sigprocmask with the
SIG_BLOCK flag, but it can block only one signal per call.
#include <signal.h>
int sighold (int sig);
sigrelse
sigrelse allows the delivery of signal sig by resetting the corresponding bit in the process
signal mask. Semantically this corresponds to the POSIX.1 function sigprocmask with the
SIG_UNBLOCK flag, but it can release only one signal per call.
#include <signal.h>
int sigrelse (int sig);
sigignore
sigignore sets the disposition of signal sig to SIG_IGNthe kernel ignores the signal.
#include <signal.h>
int sigignore (int sig);
sigpause
#include <signal.h>
int sigpause (int sig);
sigpause enables the delivery of signal sig and then waits for delivery of any signal.
CAUTION This is not the same as the BSD function sigpause described on page 190. BSD
sigpause takes a signal mask as an argument, System V sigpause takes a single signal
number. In addition, BSD sigpause only resets the mask temporarilyuntil the function
return whereas System V sigpause leaves it in this condition.
System V sigpause has a different syntax, so we need to set the signal mask explicitly with
calls to sighold, and also to release them explicitly with sigrelse
Example 135:
sighold (SIGINT); /* block SIGINT */
sighold (SIGQUIT); /* and SIGQUIT */
sigpause (0); /* wait for something to happen */
sigrelse (SIGINT); /* unblock SIGINT */
sigrelse (SIGQUIT); /* and SIGQUIT */
sigaction
sigaction is the POSIX.1 equivalent of signal. It specifies the disposition of a signal. In
addition, it can specify a mask of signals to be blocked during the processing of a signal, and
a number of flags whose meaning varies significantly from system to system.
#include <signal.h>
struct sigaction
{
void (*sa_handler)(); /* handler */
sigset_t sa_mask; /* signals to block during processing */
int sa_flags;
};
signum is the signal whose disposition is to be changed. act specifies the new disposition of
the signal, and the function returns the old disposition to oact.
If act->sa_mask is non-zero, it specifies which signals to block while the signal handler is
running. This is logically ored with the current signal mask, so it works like an implicit sig-
block on entering the signal handler.
Heres an overview of the flags:
sigprocmask
sigprocmask manipulates the process signal mask. It includes functional modes that corre-
spond to both of the BSD functions sigblock and sigsetmask:
#include <signal.h>
int sigprocmask (int how, const sigset_t *set, sigset_t *oset)
The parameter how determines how the mask is to be manipulated. It can have the following
values:
Parameter meaning
SIG_BLOCK Create a new signal mask by logically oring the current mask with the speci-
fied set.
SIG_UNBLOCK Reset the bits in the current signal mask specified in set.
SIG_SETMASK Replace the current signal mask by set.
sigpending
#include <signal.h>
int sigpending (sigset_t *set);
sigpending returns the pending signal mask to set. These are the signals pending delivery
but currently blocked, which will be delivered as soon as the signal mask allows. The return
value is an error indication and not the signal mask. This function does not have an equivalent
in any other signal implementation
sigsuspend
#include <sys/signal.h>
int sigsuspend (const sigset_t *sigmask);
sigsuspend temporarily sets the process signal mask to sigmask, and then waits for a sig-
nal. When the signal is received, the previous signal mask is restored on exit from sigsus-
pend. It always returns -1 (error), with errno set to EINTR (interrupted system call).
Well look at sigemptyset and sigaddset in the next section. Its unfortunate that this part
of the initialization looks so complicatedits just part of the explicit programming style that
POSIX.1 desires. On most systems, you could get the same effect without the calls to
sigemptyset and sigaddset by just defining
The only problem with this approach (and its a showstopper) is that its not portable: on a dif-
ferent system, sigset_t might not map to int.
#include <sys/signal.h>
struct sigstack
{
caddr_t ss_sp; /* Stack address */
int ss_onstack; /* Flag, set if currently
* executing on this stack */
};
int sigstack (const struct sigstack *ss, struct sigstack *oss);
ss may be NULL. If it is not, the process signal stack is set to ss->ss_sp, and its size is
set to ss->ss_size.
oss may also be NULL. If it is not, information about the current signal stack is returned
to it.
The structure element ss_flags may contain the following flags:
SS_DISABLE specifies that the alternate stack is to be disabled. ss_sp and
ss_size are ignored. This flag is also returned in oss when the alternate stack is
disabled.
SS_ONSTACK (returned) indicates that the process is currently executing on the alter-
nate stack. If this is the case, a modification of the stack is not possible.
What does this have to do with signals? Nothing, really, except that the receipt of a signal is
one of the most common reasons to want to perform a non-local return: a signal can interrupt
processing anywhere where the process signal mask allows it. In many cases, the result of the
signal processing is not related to the processing that was interrupted, and it may be necessary
to abort the processing and perform a non-local return. For example, if you are redisplaying
data in an X window and the size of the window changes, you will get a SIGWINCH signal.
This requires a complete recalculation of what needs to be displayed, so there is no point in
continuing the current redisplay operation.
Non-local returns are implemented with the functions setjmp, longjmp, and friends.
setjmp saves the process context and longjmp restores itin other words, it returns to the
point in the program where setjmp was called. Unlike a normal function return, a longjmp
return may involve discarding a significant part of the stack. There are a number of related
functions:
#include <setjmp.h>
The definitions of jmp_buf and sigjmp_buf are less than illuminating: they are just defined
as an array of ints long enough to contain the information that the system saves. In fact, they
contain the contents of the registers that define the process context stack pointer, frame
pointer, program counter, and usually a number of other registers.
From the user point of view, setjmp is unusual in that it can return more often than you call
it. Initially, you call setjmp and it returns the value 0. If it returns again, its because the
program called longjmp, and this time it returns the value parameter passed to longjmp,
which normally should not be 0. The caller can then use this value to determine whether this
is a direct return from setjmp, or whether it returned via longjmp:
int return_code = setjmp (env);
if (return_code)
{ /* non-0 return code: return from longjmp */
printf ("longjmp returned %d\n", return_code);
}
These functions are confusing enough in their own right, but they also have less obvious fea-
tures:
It doesnt make any sense for longjmp to return 0, and System V.4 longjmp will never
return 0, even if you tell it toit will return 1 instead. BSD longjmp will return what-
ever you tell it to.
The setjmp functions save information about the state of the function that called them.
Once this function returns, this information is no longer valid. For example, the
int mysetjmp ()
{
int a = 0;
if (a = setjmp (env))
printf ("Bombed out\n");
return a;
}
foo ()
{
...
mysetjmp (); /* catch bad errors */
...
}
The return instruction from mysetjmp to foo frees its local environment. The memory
which it occupies, and which the call to setjump saved, will be overwritten by the next
function call, so a longjmp cannot restore it.
BSD attempts to determine whether the parameter env to the longjmp functions is
invalid (such as in the example above). If it detects such an error, it will call longjm-
perror, which is intended to inform that the longjmp has failed. If longjmperror
returns, the process is aborted.
If longjmp does not recognize the error, or if the system is not BSD, the resulting
process state is indeterminate. To quote the System V.4 man page: If longjmp is called
even though env was never primed by a call to setjmp, or when the last such call was in a
function that has since returned, absolute chaos is guaranteed. In fact, the system will
probably generate a SIGSEGV or a SIGBUS, but the core dump will probably show noth-
ing recognizable.
When longjmp returns to the calling function, automatic variables reflect the last modifi-
cations made to them in the function. For example:
int foo ()
{
int a = 3;
if (setjmp (env))
{
printf ("a: %d\n", a);
return a;
}
a = 2;
longjmp (env, 4);
}
At the point where longjmp is called, the variable a has the value 2, so this function will
print a:2.
When longjmp returns to the calling function, register variables will normally have the
values they had at the time of the call to setjmp, since they have been saved in the jump
buffer. Since optimizers may reassign automatic variables to registers, this can have con-
fusing results. If you compile the example above with gcc and optimize it, it will print
a: 3. This is clearly an unsuitable situation: the solution is to declare a to be volatile
(see Chapter 20, Compilers, page 340 for more information). If we do this, a will always
have the value 2 after the longjmp.
BSD setjmp includes the signal mask in the state information it saves, but System V.4
setjmp does not save the signal mask. If you want to simulate System V.4 semantics
under BSD, you need to use _setjmp and _longjmp, which do not save the signal mask.
In either system, you can use sigsetjmp, which saves the signal mask only if save is
non-zero. Except for the type of its first parameter, the corresponding siglongjmp is
used in exactly the same manner as longjmp.
The functions must be paired correctly: if you _setjmp, you must _longjmp, and if you
setjmp you must longjmp.
kill
kill is one of the most badly named system calls in the UNIX system. Its function is to send
a signal:
#include <signal.h>
int kill (pid_t pid, int sig);
Normally, pid is the process ID of the process that should receive the signal sig. There are a
couple of additional tricks, however:
If pid is 0, the kernel sends sig to all processes whose process group ID is the same as
the group ID of the calling process.
If pid is -1, most implementations broadcast the signal to all user processes if the signal
is sent by root. Otherwise the signal is sent to all processes with the same effective user
ID. BSD does not broadcast the signal to the calling process, System V does. POSIX.1
does not define this case.
If pid is < -1, System V and BSD broadcast the signal to all processes whose process
group ID is abs (pid) (abs is the absolute value function). Again, non-root processes
are limited to sending signals to processes with the same effective user ID. BSD can also
perform this function with the call killpg.
Another frequent use of kill is to check whether a process exists: kill (pid, 0) will not
actually send a signal, but it will return success if the process exists and an error indication
otherwise.
killpg
killpg broadcasts a signal to all processes whose process group ID is abs (pid). It is sup-
plied with BSD systems:
#include <sys/signal.h>
This function sends the signal to the process group of the specified process, assuming that you
have the same effective user ID as the recipient process, or you are super-user. You can use
pid 0 to indicate your own process group. If you dont have this function, you can possibly
replace it with kill(-pgid)see the section on kill above.
raise
raise is an ANSI C function that enables a process to send a signal to itself. It is defined as
int raise (int signum);
Older systems dont have raise. You can fake it in terms of kill and getpid:
kill (getpid (), signum);
returns
Signal 11 (Segmentation fault)
Some systems supply the function psignal instead of sys_siglist. It prints the text corre-
sponding to a signal. You can get almost the same effect as the printf above by writing
char msg [80];
sprintf (msg, "Signal %d", SIGSEGV);
psignal (SIGSEGV, msg);
This mounts the file system on the disk partition /dev/usr onto the directory /usr, so if the root
directory of /dev/usr contains a file called foo, after mounting you can access it as /usr/foo.
Anything useful is bound to attract people who want to make it more useful, so it should come
as no surprise that a large number of improvements have been made to the file system in the
course of time. In the rest of this chapter, well look at the following aspects in more detail:
File systems introduced since the Seventh Edition.
Differences in function calls, starting on page 206.
Non-blocking I/O, starting on page 220.
File locking, starting on page 226.
Memory-mapped files, starting on page 232.
203
* Dont confuse the Berkeley FFS with SCOs afs, which is sometimes referred to as a Fast File System.
In fact, afs is very similar to s5fs, though later versions have symbolic links and longer file names.
Symbolic links
A symbolic link is a file whose complete contents are the name of another file. To access via
a symbolic link, you first need to find the directory entry to which it is pointing, then resolve
the link to the inode. By contrast, a traditional link (sometimes called hard link) links a file
name to an inode. Several names can point to the same inode, but it only takes one step to
find the file. This seemingly minor difference has a number of consequences:
A definite relationship exists between the original file and the symbolic link. In a normal
link, each of the file names have the same relationship to the inode; in a symbolic link,
the symbolic link name refers to the main file name. This difference is particularly obvi-
ous if you remove the original file: with a normal link, the other name still works per-
fectly. With a symbolic link, you lose the file.
Theres nothing to stop a symbolic link from pointing to another symbolic linkin fact,
its quite common, and is moderately useful. It also opens the possibility of looping: if
the second symbolic link points back to the first, the system will give up after a few itera-
tions with the error code ELOOP.
Symbolic links have two file permissions. In practice, the permission of the link itself is
of little consequencenormally it is set to allow reading, writing and execution for all
users (on an ls -l listing you see lrwxrwxrwx). The permission that counts is still the
permission of the original file.
Symbolic links allow links to different file systems, even (via NFS) to a file system on a
different machine. This is particularly useful when using read-only media, such as CD-
ROMs. See Chapter 3, Care and feeding of source trees, page 39, for some examples.
Symbolic links open up a whole new area of possible errors. Its possible for a symbolic
link to point to a file that doesnt exist, so you cant access the file, even if you have a
name and the correct permissions.
just about any system, including System V.3 and DOS, but unfortunately not XENIX. It
can offer a partial escape from the 14 character file limit, no symlinks syndrome. It is
reasonably transparent, but unfortunately does not support device files.
Remote File Sharing, rfs. This is AT&Ts answer to NFS. Although it has a number of
advantages over NFS, it is not widely used.
Along with new file systems, new file types have evolved. We have already looked at sym-
bolic links, which we can think of as a new file type. Others include FIFOs (First In First
Out) and sockets, means of interprocess communications that we looked at in Chapter 12, Ker-
nel dependencies.
In practice, you run into problems only when you port software developed under ufs, vjfs or
vxfs to a s5fs system. If you can, you should change your file system. If you cant do that,
here are some of the things that could give you headaches:
File name length. Theres very little you can do about this: if the file names are longer
than your kernel can understand, you have to change them. There are some subtle prob-
lems here: some 14-character file systems accept longer names and just silently truncate
them, others, notably SCO, signal an error. It should be fairly evident what your file sys-
tem does when you try to do it. If your system has the pathconf system call, you can
also interrogate this programmatically (see page 212).
Lack of symbolic links is another big problem. You may need far-reaching source
changes to get around this problem, which could bite you early on in the port: you may
have an archive containing symbolic links, or the configuration routines might try to cre-
ate them.
Another, more subtle difference is that BSD and System V do not agree on the question of
group ownership. In particular, when creating a file, the group ownership may be that of the
directory, or it may be that of the process that creates the file. BSD always gives the file the
group of the directory; in System V.4, it is the group of the process, unless the set group ID
bit is set in the directory permissions, in which case the file will belong to the same group as
the directory.
Function calls
The Seventh Edition left a surprising amount of functionality up to the system library. For
example, the kernel supplied no method to create a directory or rename a file. The methods
that were used to make up for these deficiencies were not always reliable, and in the course of
the time these functions have been implemented as system calls. Current systems offer the
following functions, some of them system calls:
chsize
chsize changes the end of file of an open file.
It originated in XENIX and has been inherited by System V.3.2 and System V.4. It corre-
sponds both in function and in parameters to the System V version of ftruncate: if the new
end-of-file pointer is larger than the current end-of-file pointer, it will extend the file to the
new size.
dup2
All systems offer the system call dup, which creates a copy of a file descriptor:
int dup (int oldd);
oldd is an open file descriptor; dup returns another file descriptor pointing to the same file.
The problem with dup is that you dont have any control over the number you get back: its
the numerically smallest file descriptor currently not in use. In many cases, you want a spe-
cific number. This is what dup2 does:
int dup2 (int oldd, int newd);
With newd you specify the number of the new descriptor. If its currently allocated, dup2
closes it first. You can fake this with dup, but its painful. The F_DUPFD subfunction of
fcntl does the same thing as dup2, so you can use it if it is available (see page 208). dup2 is
available on nearly every UNIX system, including the Seventh Edition. Somehow some ear-
lier versions of System V dont have it, howeverrecall that System V derived from the
Sixth Edition, not the Seventh Edition. See Chapter 1, Introduction, page 4.
You can replace them with a corresponding call to ch* if you know the name of the file asso-
ciated with the file descriptor; otherwise you could be in trouble.
fcntl
All modern versions of UNIX supply a function called fcntl, which is rather like an ioctl
for disk files:
#include <sys/fcntl.h>
As you can see from the table, arg is not always supplied, and when it is, its meaning and
type vary depending on the call.
A couple of these functions deserve closer examination:
F_SETFD and F_GETFD manipulate the close on exec flag. This is normally defined in
sys/fcntl.h as 1. Many programs use the explicit constant 1, which is theoretically non-
portable, but which works with current systems.
By default, exec inherits open files to the new program. If the close on exec flag is set,
exec automatically closes the file.
F_GETOWN and F_SETOWN have very different meanings for BSD and System V.4. In
BSD, they get and set the process ID that receives SIGIO and SIGURG signals; in System
V.4, they get and set the file owner, which can also be done with stat or fstat. There
is no direct equivalent to the BSD F_SETOWN and F_GETOWN in System V, since the
underlying implementation of non-blocking I/O is different. Instead, you call ioctl
with the I_SETSIG request see page 225 for more details.
The request F_CHKFL is defined in the System V.3 header files, but it is not documented.
F_GETFL and F_SETFL get and set the file status flags that were initally set by open. Ta-
ble 14-2 shows the flags.
Because of these compatibility problems, you dont normally use these system calls
directly you use the library call readdir instead. See the description of readdir on page
213 for more information.
getdtablesize
Sometimes its important to know how many files a process is allowed to open. This depends
heavily on the kernel implementation: some systems have a fixed maximum number of files
that can be opened, and may allow you to specify it as a configuration parameter when you
build a kernel. Others allow an effectively unlimited number of files, but the kernel allocates
space for files in groups of about 20. Evidently, the way you find out about these limits
depends greatly on the system you are running:
On systems with a fixed maximum, the constant NOFILE, usually defined in
/usr/include/sys/param.h, specifies the number of files you can open.
On systems with a configurable maximum, you will probably also find the constant
NOFILE, only you cant rely on it to be correct.
On some systems that allocate resources for files in groups, the size of these groups may
be defined in /usr/include/sys/filedesc.h as the value of the constant NDFILE.
BSD systems offer the function getdtablesize (no parameters) that returns the maxi-
mum number of files you can open.
Modern systems offer the getrlimit system call, which allows you to query a number
of kernel limits. See Chapter 12, Kernel dependencies, page 169, for details of getr-
limit.
ioctl
ioctl is a catchall function that performs functions that werent thought of ahead of time.
Every system has its own warts on ioctl, and the most common problem with ioctl is a call
with a request that the kernel doesnt understand. We cant go into detail about every ioctl
function, but we do examine terminal driver ioctl calls in some depth in Chapter 15, Terminal
drivers, starting on page 252.
lstat
lstat is a version of stat. It is identical to stat unless the pathname specifies a symbolic
link. In this case, lstat returns information about the link itself, whereas stat returns infor-
mation about the file to which the link points. BSD and System V.4 support it, and it should
be available on any system that supports symbolic links.
ltrunc
ltrunc truncates an open file in the same way that ftruncate does, but the parameters are
more reminiscent of lseek:
int ltrunc (int fd, off_t offset, int whence);
fd is the file descriptor. offset and whence specify the new end-of-file value:
If whence is SEEK_SET, ltrunc sets the file size to offset.
If whence is SEEK_CUR, ltrunc sets the file size to offset bytes beyond the current
seek position.
If whence is SEEK_END, ltrunc increases the file size by offset.
No modern mainstream system supports ltrunc. You can replace a call ltrunc (fd, off-
set, SEEK_SET) with ftruncate (fd, offset). If you have calls with SEEK_CUR and
SEEK_END, you need to first establish the corresponding offset with a call to lseek:
ftruncate (fd, lseek (fd, offset, SEEK_CUR)); or SEEK_END
#include <unistd.h>
int rmdir (const char *path)
If your system does not have the mkdir system call, you can simulate it by invoking the
open
Since the Seventh Edition, open has acquired a few new flags. All modern versions of UNIX
support most of them, but the following differ between versions:
O_NDELAY is available only in earlier versions of System V. It applies to devices and
FIFOs (see Chapter 12, Kernel dependencies, page 165, for more information on FIFOs)
and specifies that both the call to open and subsequent I/O calls should return immedi-
ately without waiting for the operation to complete. A call to read returns 0 if no data is
available, which is unfortunately also the value returned at end-of-file. If you dont have
O_NDELAY, or if this ambiguity bugs you, use O_NONBLOCK.
O_NONBLOCK specifies that both the call to open and subsequent I/O calls should return
immediately without waiting for completion. Unlike O_NDELAY, a subsequent call to
read returns -1 (error) if no data is available, and errno is set to EAGAIN.
System V.4 and 4.4BSD have a flag, called O_SYNC in System V.4 and O_FSYNC in
4.4BSD, which specifies that each call to write write should write any buffered data to
disk and update the inode. Control does not return to the program until these operations
complete. If your system does not support this feature, you can probably just remove it,
though you lose a little bit of security. To really do the Right Thing, you can include a
call to fsync after every I/O.
The parameter name is an int, not a name. Despite what it is called, it specifies the action to
perform:
name Function
_PC_LINK_MAX Return the maximum number of links that can be made to an
inode.
_PC_MAX_CANON For terminals, return the maximum length of a formatted in-
put line.
_PC_MAX_INPUT For terminals, return the maximum length of an input line.
_PC_NAME_MAX For directories, return the maximum length of a file name.
read
The function read is substantially unchanged since the Seventh Edition, but note the com-
ments about O_NDELAY and O_NONBLOCK in the section about open on page 212.
rename
Older versions of UNIX dont have a system call to rename a file: instead, they make a link
and then delete the old file. This can cause problems if the process is stopped in the middle of
the operation, and so the atomic rename function was introduced. If your system doesnt
have it, you can still do it the old-fashioned way.
revoke
revoke is used in later BSD versions to close all file descriptors associated with a special file,
even those opened by a different process. It is not available with System V.4. Typically, this
call is used to disconnect serial lines.
After a process has called revoke, a call to read on the device from any process returns an
end-of-file indication, a call to close succeeds, and all other calls fail. Only the file owner
and the super user may use this call.
};
With the introduction of ufs, which supports names of up to 256 characters, it was no longer
practical to reserve a fixed-length field for the file name, and it became more difficult to access
directories. A family of directory access routines was introduced with 4.2BSD:
#include <sys/types.h>
#include <dirent.h>
DIR *opendir (const char *filename);
struct dirent *readdir (DIR *dirp);
long telldir (const DIR *dirp);
void seekdir (DIR *dirp, long loc);
void rewinddir (DIR *dirp);
int closedir (DIR *dirp);
int dirfd (DIR *dirp);
Along with the DIR type, there is a struct dirent that corresponds to the Seventh Edition
struct direct. Unfortunately, System V defines struct dirent and DIR differently
from the original BSD implementation. In BSD, it is
struct dirent /* directory entry */
{
unsigned long d_fileno; /* file number of entry */
unsigned short d_reclen; /* length of this record */
unsigned short d_namlen; /* length of string in d_name */
char d_name [255 + 1]; /* maximum name length */
};
System V defines
struct dirent
{
ino_t d_ino; /* inode number of entry */
off_t d_off; /* offset of directory entry */
unsigned short d_reclen; /* length of this record */
char d_name [1]; /* name of file */
};
typedef struct
{
int dd_fd; /* file descriptor */
int dd_loc; /* offset in block */
int dd_size; /* amount of valid data */
System V.4 has two versions of these routines: a System V version and a BSD version. Many
reports have claimed that the BSD version is broken, though its possible that the program-
mers were using the wrong header files. If you do run into trouble, you should make sure the
header files match the flavour of dirent and DIR that you have.
Each iovec element specifies an address and the number of bytes to transfer to or from it.
The total number of bytes transferred would be the sum of the iov_len fields of all iovcnt
elements. readv and writev are available only for BSD and System V.4 systemsif you
dont have them, its relatively easy to fake them in terms of read or write. The reasons
why these calls exist at all are:
Some devices, such as tape drives, write a physical record for each call to write. This
can result in a significant drop in performance and tape capacity.
For tape drives, the only alternative is to copy the data into one block before writing.
This, too, impacts performance, though not nearly as much as writing smaller blocks.
Even for devices that dont write a physical block per write, its faster to do it in the
kernel with just a single function call: you dont have as many context switches.
struct statfs
{
short f_type; /* type of filesystem (see below) */
short f_flags; /* copy of mount flags */
long f_fsize; /* fundamental file system block size */
long f_bsize; /* optimal transfer block size */
long f_blocks; /* total data blocks in file system */
long f_bfree; /* free blocks in fs */
long f_bavail; /* free blocks avail to non-superuser */
long f_files; /* total file nodes in file system */
long f_ffree; /* free file nodes in fs */
fsid_t f_fsid; /* file system id */
long f_spare[6]; /* spare for later */
char f_mntonname[MNAMELEN]; /* mount point */
char f_mntfromname[MNAMELEN]; /* mounted filesystem */
};
typedef struct
{
long val[2];
} fsid_t;
struct statfs
{
long f_type; /* type of info, zero for now */
long f_bsize; /* fundamental file system block size */
long f_blocks; /* total blocks in file system */
long f_bfree; /* free blocks */
long f_bavail; /* free blocks available to non-super-user */
long f_files; /* total file nodes in file system */
long f_ffree; /* free file nodes in fs */
struct statvfs
{
u_long f_bsize; /* preferred file system block size */
u_long f_frsize; /* fundamental filesystem block size */
u_long f_blocks; /* total # of blocks on file system */
u_long f_bfree; /* total # of free blocks */
u_long f_bavail; /* # of free blocks available */
u_long f_files; /* total # of file nodes (inodes) */
u_long f_ffree; /* total # of free file nodes */
u_long f_favail; /* # of inodes available */
u_long f_fsid; /* file system id (dev for now) */
char f_basetype [FSTYPSZ]; /* target fs type name */
u_long f_flag; /* bit mask of flags */
u_long f_namemax; /* maximum file name length */
char f_fstr [32]; /* file system specific string */
u_long f_filler [16]; /* reserved for future expansion */
};
Theres not much to say about these functions: if you have problems, hopefully this informa-
tion will help you figure out what the author intended.
symlink
symlink creates a symbolic link in file systems that support symbolic links:
#include <unistd.h>
sysfs
sysfs is a System V function that returns information about the kinds of file systems config-
ured in the system. This function has the rather strange property of not being compatible with
ANSI Cthe parameters it accepts depend on the function supplied:
#include <sys/fstyp.h>
#include <sys/fsid.h>
This call translates fsname, a null-terminated file-system type identifier, into a file-system
type index.
int sysfs ((int) GETFSTYP, int fs_index, char *buf);
This call translates fs_index, a file-system type index, into a NUL-terminated file-system type
identifier in the buffer pointed to by buf.
int sysfs((int) GETNFSTYP);
This call returns the total number of file system types configured in the system.
These functions are available with BSD and System V.4. There is a subtle difference between
the way the BSD and System V.4 versions work: if the file is smaller than the requested
length, System V.4 extends the file to the specified length, while BSD leaves it as it is. Both
versions discard any data beyond the end if the current EOF is longer.
If your system doesnt have these functions, you may be able to perform the same function
with chsize (page 206) or the fcntl function F_FREESP (page 208).
ustat
ustat returns information about a mounted file system, and is supported by System V and
SunOS 4, but not by BSD. The call is:
struct ustat
{
daddr_t f_tfree; /* Total blocks available */
ino_t f_tinode; /* Number of free inodes */
char f_fname [6]; /* File system name */
char f_fpack [6]; /* File system pack name */
On BSD systems, you can get this information with the statfs system call, which requires a
path name instead of a device number.
utime sets the modification timestamp of the file defined by path to the time specified in
times. In the Seventh Edition, times was required to be a valid pointer, and only the file
owner or root could use the call. All newer versions of UNIX allow times to be a NULL
pointer, in which case the modification timestamp is set to the current time. Any process that
has write access to the file can use utime in this manner. BSD implements this function in
the C library in terms of the function utimes:
#include <sys/time.h>
sys/time.h defines:
struct timeval
{
long tv_sec; /* seconds */
long tv_usec; /* and microseconds */
};
int utimes (const char *file, const struct timeval *times);
#include <sys/types.h>
#include <utime.h>
utime.h defines:
struct utimbuf
{
time_t actime; /* access time */
time_t modtime; /* modification time */
};
The difference between utime and utimes is simply in the format of the access time: utime
supplies the time in time_t format, which is accurate to a second, whereas utimes uses the
timeval struct which is (theoretically) accurate to one microsecond. BSD systems supply
the utime function as a library call (which, not surprisingly, calls utimes). On XENIX and
early System V systems you can fake utimes using utime.
Non-blocking I/O
In early versions of UNIX, all device I/O was blocking: if you made a call to read and no
data was available, or if you made a call to write and the device wasnt ready to accept the
data, the process would sleep until the situation changed. This is still the default behaviour.
Blocking I/O can be restrictive in many situations, and many schemes have been devised to
allow a process to continue execution before the I/O operation completes. On current sys-
tems, you select non-blocking I/O either by supplying the flag O_NONBLOCK to open, or by
calling the fcntl function F_SETFL with the O_NONBLOCK flag (see page 209).
One problem with non-blocking I/O is that you dont automatically know when a request is
complete. In addition, if you have multiple requests outstanding, you may not really care
which finishes first, you just want to know when one finishes.
Two approaches have been used to inform a process when a request completes. One is to call
a function that returns information about current request status, and that may optionally block
until something completes. Traditionally, BSD uses select to perform this function,
whereas System V uses poll.
The other solution is to send a signal (SIGPOLL in System V, SIGIO or SIGURG in BSD) when
the request finishes. In both systems, this has the disadvantage of not supplying any informa-
tion about the request that completed, so if you have more than one request outstanding, you
still need to call select or poll to handle the situation.
select
select is called with the following parameters:
#define FD_SETSIZE 512 my maximum FD count, see below
#include <unistd.h>
#include <sys/types.h>
#include <sys/time.h>
struct timeval
{
long tv_sec; /* seconds */
long tv_usec; /* and microseconds */
};
int select (int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds,
struct timeval *timeout);
The parameters readfds, writefds, and exceptfds are bit maps, one bit per possible file
descriptor. Recall that file descriptors are small non-negative integers. select uses the file
descriptor as an index in the bit map.
This gives us a problem when porting: we dont know how many files our implementation
supports. In modern systems, there is usually no fixed limit. The solution chosen is a kludge:
choose a sufficiently high number. The expression howmany (FD_SETSIZE, NFDBITS)
evaluates to the number of words of NFDBITS required to store FD_SETSIZE bits:
#define howmany(bits, wordsize) ((bits + wordsize - 1) / wordsize)
In 4.4BSD FD_SETSIZE defaults to 256 (in sys/types.h). Nowadays, a server with many
requestors could quite easily exceed that value. Because of this, you can set it yourself: just
define FD_SETSIZE before including /usr/include/sys/types.h, as indicated in the syntax over-
view above.
Setting variables of type fd_mask is tricky, so a number of macros are supplied:
FD_SET (fd, &fdset) /* set bit fd in fdset*/
FD_CLR (fd, &fdset) /* clear bit fd in fdset */
FD_ISSET (fd, &fdset) /* return value of bit fd in fdset */
FD_ZERO (&fdset) /* clear all bits in fdset */
select examines the files specified in readfds for read completion, the files specified in
writefds for write completion and the files specified in exceptfds for exceptional condi-
tions. You can set any of these pointers to NULL if youre not interested in this kind of event.
The action that select takes depends on the value of timeout:
If timeout is a NULL pointer, select blocks until a completion occurs on one of the
specified files.
If both timeout->tv_sec and timeout->tv_usec are set to 0, select checks for
completions and returns immediately.
Otherwise select waits for completion up to the specified timeout.
select returns -1 on error conditions, and the number of ready descriptors (possibly 0) other-
wise. It replaces the contents of readfds, writefds, and exceptfds with bit maps indicat-
ing which files had a corresponding completion.
So far, we havent even mentioned nfds. Strictly speaking, its not needed: you use it to indi-
cate the number of file descriptors that are worth examining. By default, open and dup allo-
cate the lowest possible file descriptors, so select can save a lot of work if you tell it the
highest file number that is worth examining in the bit maps. Since file descriptors start at 0,
the number of file descriptors is 1 higher than the highest file descriptor number.
This baroque function has a couple of other gotchas waiting for you:
If you find a system without select that does support poll, you can probably replace
select with pollits just a SMOP.*
* To quote the New Hackers Dictionary: SMOP: /S-M-O-P/ [Simple (or Small) Matter of Program-
ming] n. 2. Often used ironically . . . when a suggestion for a program is made which seems easy to
the suggester, but is obviously (to the victim) a lot of work.
As we saw above, FD_ISSET is a macro which checks if bit fd is set in the bit mask. The
foo_completion functions do whatever is needed on completion of I/O for this file descrip-
tor. See Advanced Programming in the UNIX environment, by Richard Stevens, for further
information.
poll
poll takes a different approach from select:
#include <stropts.h>
#include <poll.h>
int poll (struct pollfd *fds, unsigned long nfds, int timeout);
For each file of interest, you set up a pollfd element with the file number and the events of
interest. events and revents are again bit maps. events can be made up of the following
values:
Event Meaning
POLLIN Data other than high priority data is available for reading.
POLLRDNORM Normal data* (priority band = 0) is available for reading.
POLLRDBAND Data from a non-zero priority band is available for reading.
POLLPRI High priority data is available for reading.
POLLOUT Normal data may be written without blocking.
POLLWRNORM The same as POLLOUT: normal data may be written without blocking.
POLLWRBAND Priority data (priority band > 0) may be written without blocking.
When it succeeds, poll sets the corresponding bits in revents to indicate which events
* STREAMS recognizes 256 different data priority bands. Normal data is sent with priority band 0, but
urgent data with a higher priority can "leapfrog" normal data. See UNIX Network Programming, by W.
Richard Stevens, for further information.
Event Meaning
POLLERR An error has occurred on the device or stream.
POLLHUP A hangup has occurred.
POLLNVAL The specified fd is not open.
Timeout processing is nearly the same as for select, but the parameter timeout is specified
in milliseconds. Since it is an int, not a pointer, you cant supply a NULL pointer; instead,
you set the value to INFTIM (defined in stropts.h) if you want the call to block.. To summa-
rize:
If timeout is set to INFTIM, poll blocks until a completion occurs on one of the speci-
fied files.
If timeout is set to 0, a check is made for completions and poll returns immediately.
If timeout is non-zero, poll waits for completion up to the specified timeout.
The code for starting the request and enabling SIGIO and SIGURG for the line assumes that the
file has been opened and the number stored in an array of file numbers.
rdchk
rdchk is a XENIX function that checks if data is available for reading on a specific file
descriptor:
int rdchk (int fd);
It returns 1 if data is available, 0 if no data is currently available, and -1 on error (and errno
is set). If you dont have it, you can implement it in terms of select or poll.
SIGPOLL
System V systems can arrange to have the signal SIGPOLL delivered when a request com-
pletes. It is not completely general: the file in question must be a STREAMS device, since
only STREAMS drivers generate the SIGPOLL signal.
The ioctl call I_SETSIG enables SIGPOLL. The third parameter specifies a bit mask of
events to wait for:
In addition to the call to ioctl, the process needs to set up a signal handler for SIG-
POLLthe default disposition is to terminate the process, which is probably not what you
want.
SIGIO
BSD systems have a similar mechanism to SIGPOLL, called SIGIO. Like SIGPOLL, it also has
its restrictions: it can be applied only to terminal or network devices. In addition, when out-
of-band data* arrives, a second signal, SIGURG, is generated. SIGIO and SIGURG are enabled
by the O_ASYNC flag to open and a couple of calls to fcntlsee page 209 for more details:
First, specify the process or process group that should receive the signals, using the
fcntl subfunction F_SETOWN in order to enable reception of SIGURG.
If you want to use SIGIO, set the O_ASYNC file status flag with the fcntl subfunction
F_SETFL.
As with System V, you need to define a signal handler for SIGIO and SIGURG.
* Sockets use the term out-of-band to refer to data which comes in at a higher priority, such as TCP
urgent mode. Like STREAMS priority data, this data will be presented ahead of normal data.
File locking
The Seventh Edition did not originally allow programs to coordinate concurrent access to a
file. If two users both had a file open for modification at the same time, it was almost impos-
sible to prevent disaster. This is an obvious disadvantage, and all modern versions of UNIX
supply some form of file locking.
Before we look at the functions that are available, its a good idea to consider the various
kinds of lock. There seem to be two of everything. First, the granularity is of interest:
file locking applies to the whole file.
range locking applies only to a range of byte offsets. This is sometimes misleadingly
called record locking.
With file locking, no other process can access the file when a lock is applied. With range
locking, multiple locks can coexist as long as their ranges dont overlap. Secondly, there are
two types of lock:
Advisory locks do not actually prevent access to the file. They work only if every par-
ticipating process ensures that it locks the file before accessing it. If the
file is already locked, the process blocks until it gains the lock.
mandatory locks prevent (block) read and write access to the file, but do not stop it from
being removed or renamed. Many editors do just this, so even manda-
tory locking has its limitations.
Finally, there are also two ways in which locks cooperate with each other:
exclusive locks allow no other locks that overlap the range. This is the only was to per-
form file locking, and it implies that only a single process can access
the file at a time. These locks are also called also called write locks.
shared locks allow other shared locks to coexist with them. Their main purpose is to
prevent an exclusive lock from being applied. In combination with
mandatory range locking, a write is not permitted to a range covered by
a shared lock. These locks are also called read locks.
There are five different kinds of file or record locking in common use:
Lock files, also called dot locking, is a primitive workaround used by communication pro-
grams such as uucp and getty. It is independent of the system platform, but since it is
frequently used well look at it briefly. It implements advisory file locking.
After the initial release of the Seventh Edition, a file locking package using the system
call locking was introduced. It is still in use today on XENIX systems. It implements
mandatory range locking.
BSD systems have the system call flock. It implements advisory file locking.
System V, POSIX.1, and more recent versions of BSD support range locking via the
fcntl system call. BSD and POSIX.1 systems provide only advisory locking. System
V supplies a choice of advisory or mandatory locking, depending on the file permissions.
If you need to rewrite locking code, this is the method you should use.
System V also supplies range locking via the lockf library call. Again, it supplies a
choice of advisory or mandatory locking, depending on the file permissions.
The decision between advisory and mandatory locking in System V depends on the file per-
missions and not on the call to fcntl or lockf. The setgid bit is used for this purpose. Nor-
mally, in executables, the setgid bit specifies that the executable should assume the effective
group ID of its owner group when execed. On files that do not have group execute permis-
sion, it specifies mandatory locking if it is set, and advisory locking if it is not set. For exam-
ple,
A file with permissions 0764 (rwxrw-r--) will be locked with advisory locking, since
its permissions include neither group execute nor setgid.
A file with permissions 0774 (rwxrwxr--) will be locked with advisory locking, since
its permissions dont include setgid.
A file with permissions 02774 (rwxrwsr--) will be locked with advisory locking, since
its permissions include both group execute and setgid.
A file with permissions 02764 will be locked with mandatory locking, since it has the
setgid bit set, but group execute is not set. If you list the permissions of this file with ls
-l, you get rwxrwlr-- on a System V system, but many versions of ls, including BSD
and GNU versions, will list rwxrwSr--.
Lock files
Lock files are the traditional method that uucp uses for locking serial lines. Serial lines are
typically used either for dialing out, for example with uucp, or dialing in, which is handled by
a program of the getty family. Some kind of synchronization is needed to ensure that both of
these programs dont try to access the line at the same time. The other forms of locking we
describe only apply to disk files, so we cant use them. Instead, uucp and getty create lock
files. A typical lock file will have a name like /var/spool/uucp/LCK..ttyb, and for some reason
these double periods in the name have led to the term dot locking.
The locking algorithm is straightforward: if a process wants to access a serial line /dev/ttyb, it
looks for a file /var/spool/uucp/LCK..ttyb. If it finds it, it checks the contents, which specify
the process ID of the owner, and checks if the owner still exists. If it does, the file is locked,
and the process cant access the serial line. If the file doesnt exist, or if the owner no longer
exists, the process creates the file if necessary and puts its own process ID in the file.
Although the algorithm is straightforward, the naming conventions are anything but standard-
ized. When porting software from other platforms, it is absolutely essential that all programs
using dot locking should be agreed on the lock file name and its format. Lets look at the lock
file names for the device /dev/ttyb, which is major device number 29, minor device number 1.
The ls -l listing looks like:
$ ls -l /dev/ttyb
crw-rw-rw- 1 root sys 29, 1 Feb 25 1995 /dev/ttyb
locking locks a block of data of length size bytes, starting at the current position in the file.
Parameter Meaning
LK_LOCK Obtain an exclusive lock for the specified block. If any part is not avail-
able, sleep until it becomes available.
LK_NBLCK Obtain an exclusive lock for the specified block. If any part is not avail-
able, the request fails, and errno is set to EACCES.
LK_NBRLCK Obtains a shared lock for the specified block. If any part is not available,
the request fails, and errno is set to EACCES.
LK_RLCK Obtain a shared lock for the specified block. If any part is not available,
sleep until it becomes available.
LK_UNLCK Unlock a previously locked block of data.
flock
flock is the weakest of all the lock functions. It provides only advisory file locking.
#include <sys/file.h>
(defined in sys/file.h)
#define LOCK_SH 1 /* shared lock */
#define LOCK_EX 2 /* exclusive lock */
#define LOCK_NB 4 /* dont block when locking */
#define LOCK_UN 8 /* unlock */
flock applies or removes a lock on fd. By default, if a lock cannot be granted, the process
blocks until the lock is available. If you set the flag LOCK_NB, flock returns immediately
with errno set to EWOULDBLOCK if the lock cannot be granted.
fcntl locking
On page 207 we discussed fcntl, a function that can perform various functions on open files.
A number of these functions perform advisory record locking, and System V also offers the
option of mandatory locking. All locking functions operate on a struct flock:
struct flock
{
short l_type; /* lock type: read/write, etc. */
short l_whence; /* type of l_start */
off_t l_start; /* starting offset */
off_t l_len; /* len = 0 means until end of file */
long l_sysid; /* Only SVR4 */
pid_t l_pid; /* lock owner */
};
In this structure,
l_type specifies the type of the lock, listed in Table 14-9.
value Function
F_RDLCK Acquire a read or shared lock.
F_WRLCK Acquire a write or exclusive lock.
F_UNLCK Clear the lock.
The offset is specified in the same way as a file offset is specified to lseek:
flock->l_whence may be set to SEEK_SET (offset is from the beginning of the file),
SEEK_CUR (offset is relative to the current position) or SEEK_EOF (offset is relative to the
current end of file position).
All fcntl lock operations use this struct, which is passed to fcntl as the arg parameter. For
example, to perform the operation F_FOOLK, you would write:
struct flock flock;
error = fcntl (myfile, F_FOOLK, &flock);
lockf
lockf is a library function supplied only with System V. Like fcntl, it implements advisory
or mandatory range locking based on the file permissions. In some systems, it is implemented
in terms of fcntl. It supports only exclusive locks:
#include <unistd.h>
The functions are similar to those supplied by fcntl. l_type specifies the type of the lock,
as shown in Table 14-10.
value Function
F_ULOCK Unlock the range.
F_LOCK Acquire exclusive lock.
F_TLOCK Lock if possible, otherwise return status.
F_TEST Check range for other locks.
lockf does not specify a start offset for the range to be locked. This is always the current
position in the fileyou need to use lseek to get there if you are not there already. The fol-
lowing code fragments are roughly equivalent:
flock->ltype = F_WRLK; /* lockf only supports write locks */
flock->whence = SEEK_SET;
flock->l_start = filepos; /* this was set elsewhere */
flock->l_len = reclen; /* the length to set */
error = fcntl (myfile, F_GETLK, &flock);
...and
lseek (myfile, SEEK_SET, filepos); /* Seek the correct place in the file */
error = lockf (myfile, F_LOCK, reclen);
If your system doesnt have fcntl locking, you will almost certainly have either flock
or lockf locking instead. If the package supports it, use it. Pure BSD systems dont
support lockf, but some versions simulate it. Since lockf can also be used to require
mandatory locking, its better to use flock on BSD systems and lockf on System V
systems.
Youll probably not come across any packages which support locking. If you do, and
your system supports it, its not a bad choice.
If all else fails, use lock files. This is a very poor option, thoughits probably a better
idea to consider a more modern kernel.
Memory-mapped files
Some systems offer a feature called memory mapped files: the data of a file is mapped to a
particular area of memory, so you can access it directly rather than by calling read and
write. This increases performance, since the virtual memory system is more efficient than
the file system. The following function calls are used to implement memory mapping:
You need to open the file with the file system calls open or creat.
mmap maps the file into memory.
msync ensures that updates to the file map are flushed back to the file.
munmap frees the mapped file data.
In the following sections, well look at these functions more closely.
mmap
mmap maps a portion of a file to memory.
#include <sys/types.h>
#include <sys/mman.h>
caddr_t mmap (caddr_t addr, int len, int prot, int flags, int fd, off_t offset);
addr specifies the address at which the file should be mapped. Unless you have good
reasons to do otherwise, you should specify it as NULL and let mmap choose a suitable
address itself. If mmap cant place the memory where it is requested, the subsequent be-
haviour depends on the flag MAP_FIXEDsee the discussion of flags below.
len specifies the length to map.
prot specifies the accessibility of the resultant memory region, and may be any combi-
nation of PROT_EXEC (pages may be executed), PROT_READ (pages may be read) or
PROT_WRITE (pages may be written). In addition, System V.4 allows the specification
PROT_NONE (pages may not be accessed at all).
flags is a bit map that specifies properties of the mapped region. It consists of a combi-
nation of the following bit-mapped flags:
MAP_ANON specifies that the memory is not associated with any specific file. In
many ways, this is much the same thing as a call to malloc: you get an area of
memory with nothing in it. This flag is available only in BSD.
MAP_FILE specifies that the region is mapped from a regular file or character-spe-
cial device. This flag, supplied only in BSD, is really a dummy and is used to indi-
cate the opposite of MAP_ANON: if you dont have it, ignore it.
MAP_FIXED specifies that mmap may use only the specified addr as the address of
the region. The 4.4BSD man page discourages the use of this option.
MAP_INHERIT permits regions to be inherited across exec system calls. Only sup-
ported in 4.4BSD.
MAP_PRIVATE specifies that modifications to the region are private: if the region is
modified, a copy of the modified pages is created and the modifications are copied
to them. This flag is used in debuggers and to perform page-aligned memory allo-
cations: malloc doesnt allow you to specify the address you want. In some sys-
tems, such as System V.4, MAP_PRIVATE is defined as 0, so this is the default behav-
iour. In others, such as SunOS 4, you must specify either MAP_PRIVATE or
MAP_SHAREDotherwise the call fails with an EINVAL error code.
MAP_SHARED specifies that modifications to the region are shared: the virtual mem-
ory manager writes any modifications back to the file.
On success, mmap returns the address of the area that has been mapped. On failure, it
returns -1 and sets errno.
msync
Writes to the memory mapped region are treated like any other virtual memory access: the
page is marked dirty, and thats all that happens immediately. At some later time the memory
manager writes the contents of memory to disk. If this file is shared with some other process,
you may need to explicitly flush it to disk, depending on the underlying cooperation between
the file system and the virtual memory manager.
System V.4 maps the pages at a low level, and the processes share the same physical page, so
this problem does not arise. BSD and older versions of System V keep separate copies of
memory mapped pages for each process that accesses them. This makes sharing them diffi-
cult. On these systems, the msync system call is used to flush memory areas to disk. This
solution is not perfect: the possibility still exists that a concurrent read of the area may get a
garbled copy of the data. To quote the 4.4BSD man pages:
Any required synchronization of memory caches also takes place at this time. Filesystem oper-
ations on a file that is mapped for shared modifications are unpredictable except after an
msync.
addr must be specified and must point to a memory mapped page; len may be 0, in which
case all modified pages are flushed. If len is not 0, only modified pages in the area defined
by addr and len are flushed.
munmap
munmap unmaps a memory mapped file region:
void munmap (caddr_t addr, int len);
It unmaps the memory region specified by addr and len. This is not necessary before termi-
nating a programthe region is unmapped like any other on terminationand it carries the
danger that modifications may be lost, since it doesnt flush the region before deallocating.
About the only use is to free the area for some other operation.
The terminal handler has clearly entered the race for ever-greater complexity and generality.
Its still not complex and general enough for TENEX fans.
235
The ioctl request interface to the terminal driver, one of the favourite problem areas in
porting terminal-related software.
The POSIX.1 termios request interface.
The documentation of every driver describes at least two different modes of treating terminal
input. The Seventh Edition and BSD drivers define three:
In raw mode, the read system call passes input characters to the caller exactly as they
are entered. No processing takes place in the driver. This mode is useful for programs
which want to interpret characters themselves, such as full-screen editors.
cooked mode interprets a number of special characters, including the new line character
\n. A read call will terminate on a \n. This is the normal mode used by programs that
dont want to be bothered by the intricacies of terminal programming.
cbreak mode performs partial interpretation of the special characters, this time not
including \n. cbreak mode is easier to use than raw mode, and is adequate for many pur-
poses. Its a matter of taste whether you prefer this to raw mode or not.
By contrast, termio and termios specify two different processing modes for terminal input:
canonical * mode performs significant processing on input before passing it to the calling
function. Up to 21 input special characters may be used to tell the driver to do things as
varied as start and stop output, to clear the input buffer, to send signals to the process and
to terminate a line in a number of different ways.
Non-canonical input mode, in which the driver does not interpret input characters spe-
cially (this corresponds roughly to BSD cbreak mode).
In fact, subdividing the terminal operation into modes is an oversimplification: a large number
of flags modify the operational modes. Later in the chapter well look at how to set these
modes with termios.
* The word canon refers to (religious) law: the intent is that this should be the correct or standard way to
handle input characters. See the New Hackers Dictionary for a long discussion of the term.
With the System V termio driver, it would look like Example 15-2:
Example 152:
struct termio initial_status; /* initial termio flags */
struct termio noicanon_status; /* and the same with icanon reset */
Dont rely on code like this to be termio code: termios code can look almost identical. Cor-
rect termios code uses the termios functions which we will look at on page 265, and looks like
Example 15-3:
Example 153:
struct termios initial_status; /* initial termios flags */
struct termios noicanon_status; /* and the same with icanon reset */
Terminology
Before we start, its a good idea to be clear about a few terms that are frequently confused:
All terminal drivers buffer I/O in two queues, an input queue and an output queue. The
input queue contains characters that the user has entered and the process has not yet read.
The output queue contains characters that the process has written but that have not yet
been output to the terminal. These queues are maintained inside the terminal driver.
Dont confuse them with buffers maintained in the process data space by the stdio rou-
tines.
The term flush can mean to discard the contents of a queue, or to wait until they have all
been output to the terminal. Most of the time it means to discard the contents, and thats
how well use it in this chapter.
The term drain means to wait until the contents of the output queue have been written to
the terminal. This is also one of the meanings of flush.
Special characters, frequently called control characters, are input characters that cause
the terminal driver to do something out of the ordinary. For example, CTRL-D usually
causes the terminal driver to return an end-of-file indication. The term special charac-
ters is the better term, since you can set them to characters that are not ASCII control
characters. For example, even today, the default erase character in System V is #: its a
special character, but not an ASCII control character.
The baud rate of a modem is the number of units of information it can transmit per sec-
ond. Modems are analogue devices that can represent multiple bits in a single unit of
information modern modems encode up to 6 bits per unit. For example, a modern
V.32bis modem will transfer 14400 bits per second, but runs at only 2400 baud. Baud
rates are of interest only to modem designers.
As the name indicates, the bit rate of a serial line indicates how many bits it can transfer
per second. Bit rates are often erroneously called baud rates, even in official documenta-
tion. The number of bytes transferred per second depends on the configuration: nor-
mally, an asynchronous serial line will transmit one start bit and one stop bit in addition
to the data, so it transmits 10 bits per byte.
break is an obsolescent method to signal an unusual condition over an asynchronous line.
Normally, a continuous voltage or current is present on a line except when data is being
transferred. Break effectively breaks (disconnects) the line for a period between .25 and
.5 second. The serial hardware detects this and reports it separately. One of the prob-
lems with break is that it is intimately related to the serial line hardware.
DCE and DTE mean data communication equipment and data terminal equipment
respectively. In a modem connection, the modem is the DCE and both terminal and
computer are DTEs. In a direct connect, the terminal is the DTE and the computer is the
DCE. Different cabling is required for these two situations.
RS-232, also known as EIA-232, is a standard for terminal wiring. In Europe, it is some-
times referred to as CCITT V.24, though V.24 does not in fact correspond exactly to
RS-232. It defines a number of signals, listed in Table 15-1.
For more details about RS-232, see RS-232 made easy, second edition by Martin Seyer.
The bit rates in sg_ispeed and sg_ospeed are encoded, and allow only a certain number of
speeds:
The field sg_flags contains a bit map specifying the following actions:
A second structure defines additional special characters that the driver interprets in cooked
mode. They are stored in a struct tchars, which is also defined in /usr/include/sgtty.h:
struct tchars
{
char t_intrc; /* interrupt (default DEL) */
char t_quitc; /* quit (default \) */
char t_startc; /* start output (default Q)*/
char t_stopc; /* stop output (default S) */
char t_eofc; /* end-of-file (default D) */
char t_brkc; /* input delimiter (like nl, default -1) */
};
Each of these characters can be disabled by setting it to -1 (octal 0377), as is done with the
default t_brkc. This means that no key can invoke its effect.
The variable c_line specifies the line discipline. It is defined in termio, and not in the
POSIX.1 termios standard, but some System V versions of termios have it anyway. NCC is the
number of special characters. Well look at them after the flags.
Not all versions of System V define the members c_ispeed and c_ospeed. Instead, they
encode the line speed in c_cflag. The correct way to access them is via the termios utility
functions cfgetispeed, cfsetispeed, cfgetospeed, cfsetospeed and cfsetspeed,
which we will discuss on page 265. To make matters worse, some older System V termios
implementations supplied c_ispeed and c_ospeed, but the implementation didnt use them.
In addition, many systems cannot handle different input and output speeds, so setting one
c_iflag
c_iflag specifies how the driver treats terminal input:
for backward compatibility with XENIX. Some other versions of System V dont define
them at all, and BSD systems and yet other System V systems supply them in c_cflags,
where they belong.
c_oflag specifies the behaviour on output.
* The ASCII character represented by binary 0 (the C character constant \0). Not to be confused with
the null pointer, which in C is usually called NULL.
special characters
The number of special characters has increased from 6 in the Seventh Edition (struct
tchars) to 8 in termio and a total of 20 in termios (though 4 of the termios special characters
are reserved in other words, not defined). Despite this number, there is no provision for
redefining CR and NL.
Index in Index in
c_cc Default c_cc Default
Name (SysV) (SysV) (BSD) (BSD) Function
CR (none) \r (none) \r Go to beginning of line. In
canonical and cooked modes,
complete a read request.
NL (none) \n (none) \n End line. In canonical and
cooked modes, complete a read
request.
VINTR 0 DEL 8 CTRL-C Generate an SIGINT signal.
You will frequently see these names without the leading V. For example, the stty program
refers to VQUIT as QUIT.
Canonical mode
To quote Richard Stevens Advanced Programming in the UNIX environment: Canonical
mode is simpleit takes only about 30 pages for a brief description. For an even simpler
description: everything in the rest of this chapter applies to canonical mode unless otherwise
stated.
Non-canonical mode
Non-canonical mode ignores all special characters except INTR, QUIT, SUSP, STRT, STOP,
DISCARD and LNEXT. If you dont want these to be interpreted, you can disable them by
setting the corresponding entry in tchars to _POSIX_VDISABLE.
The terminal mode has a strong influence on how a read from a terminal completes. In canon-
ical mode, a read request will complete when the number of characters requested has been
input, or when the user enters one of the characters CR, NL, VEOL or (where supported)
VEOL2. In non-canonical mode, no special character causes a normal read completion. The
way a read request completes depends on two variables, MIN and TIME. MIN represents a
minimum number of characters to be read, and TIME represents a time in units of 0.1 second.
There are four possible cases:
1. Both MIN and TIME are non-zero. In this case, a read will complete when either MIN
characters have been entered or TIME/10 seconds have passed since a character was
entered. The timer starts when a character is entered, so at least one character must be
entered for the read to complete.
2. MIN is non-zero, TIME is zero. In this case, the read will not complete until MIN char-
acters have been entered.
3. MIN is zero and TIME is non-zero. The read will complete after entering one character
or after TIME/10 seconds. In the latter case, 0 characters are returned. This is not the
same as setting MIN to 1 and leaving TIME as it is: in this case, the read would not
Raw mode
Raw mode does almost no interpretation of the input stream. In particular, no special charac-
ters are recognized, and there is no timeout. The non-canonical mode variables MIN and
TIME do not exist. The result is the same as setting MIN to 1 and TIME to 0 in non-canonical
mode.
Cooked mode
The cooked mode of the old terminal driver is essentially the same as canonical mode, within
the limitations of the driver data structurestermios offers some features that are not avail-
able with the old terminal driver, such as alternate end-of-line characters.
Cbreak mode
To quote the Seventh Edition manual:
CBREAK is a sort of half-cooked (rare?) mode.
In terms of termios, it is quite close to non-canonical mode: the only difference is that cbreak
mode turns off echo. Non-canonical mode does not specify whether echo is on or off.
parameters are set. You can set them to whatever you feel appropriate.
ioctl
ioctl is the file system catchall: if there isnt any other function to do the job, then somebody
will bend ioctl to do it. Nowhere is this more evident than in terminal I/O handling. As a
result of this catchall nature, its not easy to represent ioctl parameters in C.
Well look at the semantics first. The ioctl function call takes three parameters:
1. A file number.
2. A request, which well look at in more detail in the next section.
3. When present, the meaining is defined by the request. It could be an integer, another
request code or a pointer to some structure defined by the request.
Bit 31 29 28 16 15 8 7 0
type length ioctl type function subcode
The first three bits specify the type of parameter. IOC_VOID (0x20 in the first byte) spec-
ifies that the request takes no parameters, IOC_OUT (0x40 in the first byte) specifies that
the parameters are to be copied out of the kernel (in other words, that the parameters are
to be returned to the user), and IOC_IN (0x80 in the first byte) specifies that the parame-
ters are to be copied in to the kernel (they are to be passed to ioctl).
The next 13 bits specify the length of the parameter in bytes.
The next byte specifies the type of request. This is frequently a mnemonic letter. In
4.4BSD, this field is set to the lower-case letter t for terminal ioctls.
Finally, the last byte is a number used to identify the request uniquely.
This encoding depends heavily on the operating system. Other systems (especially, of course,
16 bit systems) encode things differently, but the general principle remains the same.
Both the request code and the third parameter, where present, do not map easily to C language
data structures. As a result, the definition of the function varies significantly. For example,
XENIX and BSD declare it as:
#include <sys/ioctl.h>
int ioctl (int fd, unsigned long request, char *argp)
#include <unistd.h>
int ioctl (int fs, int request, /* arg */ ...);
Strictly speaking, since the request code is not a number, both int and unsigned long are
incorrect, but they both do the job.
When debugging a program, its not always easy to determine which request has been passed
to ioctl. If you have the source code, you will see something like
ioctl (stdin, TIOCGETA, &termstat);
Unfortunately, a number of ioctl calls are embedded in libraries to which you probably
dont have source, but you can figure out whats going on by setting a breakpoint on ioctl.
In this example, when you hit the breakpoint, you will see something like:
(gdb) bt
#0 ioctl (file=0, request=1076655123, parameter=0xefbfd58c "") at ioctl.c:6
#1 0x10af in main () at foo.c:12
The value of request looks completely random. In hexadecimal it starts to make a little
more sense:
(gdb) p/x request
$1 = 0x402c7413
If we compare this with the request code layout in the example above, we can recognize a fair
amount of information:
The first byte starts with 0x40, IOC_OUT: the parameter exists and defines a return value.
The next 13 bits are 0x2c, the length to be returned (this is the length of struct
termios).
The next byte is 0x74, the ASCII character t, indicating that this is a terminal ioctl
request.
The last byte is 0x13 (decimal 19).
Its easy enough to understand this when its deciphered like this, but doing it yourself is a lot
different. The first problem is that there is no agreed place where the ioctl requests are
defined. The best place to start is in the header file sys/ioctl.h, which in the case of 4.4BSD
will lead you to the file sys/ioccom.h (sys/sys/ioccom.h in the 4.4BSD distribution). Here you
will find code like:
#define IOCPARM_MASK 0x1fff /* parameter length, at most 13 bits */
#define IOCPARM_LEN(x) (((x) >> 16) & IOCPARM_MASK)
#define IOCBASECMD(x) ((x) & (IOCPARM_MASK << 16))
#define IOCGROUP(x) (((x) >> 8) & 0xff)
#define IOC_VOID 0x20000000 /* no parameters */
#define IOC_OUT 0x40000000 /* copy out parameters */
#define IOC_IN 0x80000000 /* copy in parameters */
These define the basic parts of the request. Next come the individual types of request:
With these building blocks, we can now understand the real definitions:
#define TIOCSBRK _IO(t, 123) /* set break bit */
#define TIOCCBRK _IO(t, 122) /* clear break bit */
#define TIOCSDTR _IO(t, 121) /* set data terminal ready */
#define TIOCCDTR _IO(t, 120) /* clear data terminal ready */
#define TIOCGPGRP _IOR(t, 119, int) /* get pgrp of tty */
#define TIOCSPGRP _IOW(t, 118, int) /* set pgrp of tty */
These define four requests without parameters (_IO), a request that returns an int parameter
from the kernel (_IOR), and a request that passes an int parameter to the kernel (_IOW).
Terminal ioctls
For a number of reasons, its difficult to categorize terminal driver ioctl calls:
As the terminal driver has changed over the course of time, some implementors have
chosen to keep the old ioctl codes and give them new parameters. For example, the
Seventh Edition call TIOCGETA returned the terminal parameters to a struct sgttyb.
The same call in System V returns the values to a struct termio, and in 4.4BSD it
returns the values to a struct termios.
The documentation for many ioctl calls is extremely hazy: although System V supports
the old terminal driver discipline, the documentation is very scant. Just because an
ioctl function is not documented in the man pages doesnt mean that it isnt supported:
its better to check in the header files (usually something like sys/termio.h or
sys/termios.h).
Many ioctl calls seem to duplicate functionality. There are minor differences, but even
they are treacherous. For example, in the Seventh Edition the TIOCSETA function drains
the output queue and discards the content of the input queue before setting the terminal
state. The same function in 4.4BSD performs the function immediately. To get the Sev-
enth Edition behaviour, you need to use TIOCSETAF. The behaviour in System V is not
documented, which means that you may be at the mercy of the implementor of the device
driver: on one system, it may behave like the Seventh Edition, on another like 4.4BSD.
In the following sections, well attempt to categorize the most frequent ioctl functions in the
kind of framework that POSIX.1 uses for termios. Heres an index to the mess:
Terminal attributes
One of the most fundamental groups of ioctl requests get and set the terminal state. This
area is the biggest mess of all. Each terminal driver has its own group of requests, the request
names are similar enough to be confusing, different systems use the same request names to
mean different things, and even in termios, there is no agreement between BSD and System V
about the names of the requests.
Table 15-12 gives an overview.
TIOCGETA
The call ioctl (fd, TIOCGETA, term) places the current terminal parameters in the struc-
ture term. The usage differs depending on the system:
In the Seventh Edition, term was of type struct sgttyb *.
In System V, term is of type struct termio *.
TIOCSETA
The call ioctl (fd, TIOCSETA, term) sets the current terminal state from term. The
usage differs depending on the system:
In the Seventh Edition, term was of type struct sgttyb *. The system drained the
output queue and flushed the input queue before setting the parameters.
In System V.3, term is of type struct termio *. The drain and flush behaviour is not
documented.
In 4.4BSD, term is of type struct termios *. The action is performed immediately
with no drain or flush. This is used to implement the tcsetattr function with the
TCSANOW option.
TIOCSETAW
The call ioctl (fd, TIOCSETAW, void *term) waits for any output to complete, then
sets the terminal state associated with the device. 4.4BSD uses this call to implement the
tcsetattr function with the TCSADRAIN option. In XENIX, the parameter term is of type
struct termio; in other systems is it of type struct termios.
TIOCSETAF
The call ioctl (fd, TIOCSETAF, void *term) waits for any output to complete, flushes
any pending input and then sets the terminal state. 4.4BSD uses this call to implement the
tcsetattr function with the TCSAFLUSH option. In XENIX, the parameter term is of type
struct termio, in other systems is it of type struct termios.
TIOCSETN
The call ioctl (fd, TIOCSETN, struct sgttyb *term) sets the parameters but does
not delay or flush input. This call is supported by System V.3. and the Seventh Edition. In
the Seventh Edition, this function works only on the first 6 bytes of the sgttyb structure.
TIOCHPCL
The call ioctl (fd, TIOCHPCL, NULL) specifies that the terminal line is to be discon-
nected (hung up) when the file is closed for the last time.
TIOCGETC
The call ioctl (fd, TIOCGETC, struct tchars *chars) returns the terminal special
characters to chars.
TIOCSETC
The call ioctl (fd, TIOCSETC, struct tchars *chars) sets the terminal special char-
acters from chars.
TCGETS
The call ioctl (fd, TCGETS, struct termios *term) returns the current terminal
parameters to term. This function is supported by System V.4.
TCSETS
The call ioctl (fd, TCSETS, struct termios *term) immediately sets the current ter-
minal parameters from term. This function is supported by System V.4 and corresponds to
the 4.4BSD call TIOCSETA.
TCSETSW
The call ioctl (fd, TCSETSW, struct termios *term) sets the current terminal
parameters from term after all output characters have been output. This function is supported
by System V.4 and corresponds to the 4.4BSD call TIOCSETAW.
TCSETSF
The call ioctl (fd, TCSETSF, struct termios *term) flushes the input queue and
sets the current terminal parameters from term after all output characters have been output.
This function is supported by System V.4 and corresponds to the 4.4BSD call TIOCSETAF.
TCGETA
The call ioctl (fd, TCGETA, struct termio *term) stores current terminal parame-
ters in term. Not all termios parameters can be stored in a struct termio; you may find
it advantageous to use TCGETS instead (see above).
TCSETA
The call ioctl (fd, TCSETA, struct termio *term) sets the current terminal status
from term. Parameters that cannot be stored in struct termio are not affected. This corre-
sponds to TCSETA, except that it uses a struct termio * instead of a struct termios *.
TCSETAW
The call ioctl (fd, TCSETAW, struct termio *term) sets the current terminal param-
eters from term after draining the output queue. This corresponds to TCSETW, except that it
uses a struct termio * instead of a struct termios *.
TCSETAF
The call ioctl (fd, TCSETAF, struct termio *term) input queue flushes the input
queue and sets the current terminal parameters from term after all output characters have
been output. This corresponds to TCSETF, except that it uses a struct termio * instead of
a struct termios *.
TIOCGWINSZ
The call ioctl (fd, TIOCGWINSZ, struct winsize *ws) puts the window size infor-
mation associated with the terminal in ws. The window size structure contains the number of
rows and columns (and pixels if appropiate) of the devices attached to the terminal. It is set
by user software and is the means by which most full screen oriented programs determine the
screen size. The winsize structure is defined as:
struct winsize
{
unsigned short ws_row; /* rows, in characters */
unsigned short ws_col; /* columns, in characters */
unsigned short ws_xpixel; /* horizontal size, pixels */
unsigned short ws_ypixel; /* vertical size, pixels */
};
Many implementations ignore the members ws_xpixel and ws_ypixel and set them to 0.
TIOCSWINSZ
The call ioctl (fd, TIOCSWINSZ, struct winsize *ws) sets the window size associ-
ated with the terminal to the value at ws. If the new size is different from the old size, a SIG-
WINCH (window changed) signal is sent to the process group of the terminal. See TIOCG-
WINSZ for more details.
TIOCSETD
The call ioctl (fd, TIOCSETD, int *ldisc); changes the line discipline to ldisc.
Not all systems support multiple line disciplines, and both the available line disciplines and
their names depend on the system. Here are some typical ones:
TIOCGETD
The call ioctl (fd, TIOCGETD, int *ldisc) returns the current line discipline at
ldisc. See the discussion in the section on TIOCSETD above.
Hardware control
TIOCSBRK
The call ioctl (fd, TIOCSBRK, NULL) sets the terminal hardware into break condition.
This function is supported by 4.4BSD.
TIOCCBRK
The call ioctl (fd, TIOCCBRK, NULL) clears a terminal hardware BREAK condition.
This function is supported by 4.4BSD.
TIOCSDTR
The call ioctl (fd, TIOCSDTR, NULL) asserts Data Terminal Ready (DTR). This func-
tion is supported by 4.4BSD. See page 239 for details of the DTR signal.
TIOCCDTR
The call ioctl (fd, TIOCCDTR, NULL) resets Data Terminal Ready (DTR). This function
is supported by 4.4BSD. See page 239 for details of the DTR signal.
TIOCMSET
The call ioctl (fd, TIOCMSET, int *state) sets modem state. It is supported by
4.4BSD, SunOS and System V.4, but not all terminals support this call. *state is a bit map
representing the parameters listed in table Table 15-13:
Parameter meaning
TIOCM_LE Line Enable
TIOCM_DTR Data Terminal Ready
TIOCM_RTS Request To Send
TIOCM_ST Secondary Transmit
TIOCM_SR Secondary Receive
TIOCM_CTS Clear To Send
TIOCM_CAR Carrier Detect
TIOCM_CD Carrier Detect (synonym)
TIOCM_RNG Ring Indication
TIOCM_RI Ring Indication (synonym)
TIOCM_DSR Data Set Ready
TIOCMGET
The call ioctl (fd, TIOCMGET, int *state) returns the current state of the terminal
modem lines. See the description of TIOCMSET for the use of the bit mapped variable state.
TIOCMBIS
The call ioctl (fd, TIOCMBIS, int *state) sets the modem state in the same manner
as TIOMSET, but instead of setting the state bits unconditionally, each bit is logically ored
with the current state.
TIOCMBIC
The call ioctl (fd, TIOCMBIC, int *state) clears the modem state: each bit set in the
bitmap state is reset in the modem state. The other state bits are not affected.
TCSBRK
The call ioctl (fd, TCSBRK, int nobreak) drains the output queue and then sends a
break if nobreak is not set. This function is supported in System V and SunOS. In contrast
to the 4.4BSD function TIOCSBRK, TCSBRK resets the break condition automatically.
TCXONC
The call ioctl (fd, TCXONC, int type) specifies flow control. It is supported in System
V and SunOS. Table 15-14 shows the possible values of type.
Queue control
TIOCOUTQ
The call ioctl (fd, TIOCOUTQ, int *num) sets the current number of characters in the
output queue to *num. This function is supported by BSD and SunOS.
TIOCSTI
The call ioctl (fd, TIOCSTI, char *cp) simulates typed input. It inserts the character
at *cp into the input queue. This function is supported by BSD and SunOS.
TIOCSTOP
The call ioctl (fd, TIOCSTOP, NULL) stops output on the terminal. Its like typing
CTRL-S at the keyboard. This function is supported by 4.4BSD.
TIOCSTART
The call ioctl (fd, TIOCSTART, NULL) restarts output on the terminal, like typing CTRL-
Q at the keyboard. This function is supported by 4.4BSD.
TIOCDRAIN
The call ioctl (fd, TIOCDRAIN, NULL) suspends process execution until all output is
drained. This function is supported by 4.4BSD.
TIOCFLUSH
The call ioctl (fd, TIOCFLUSH, int *what) flushes the input and output queues. This
function is supported by 4.4BSD, System V.3 and the Seventh Edition. The System V.3 and
Seventh Edition implementations ignore the parameter what and flush both queues. 4.4BSD
flushes the queues if the corresponding bits FREAD and FWRITE are set in *what. If no bits
are set, it clears both queues.
TCFLSH
The call ioctl (fd, TCFLSH, int type) flushes the input or output queues, depending
on the flags defined in Table 15-15.
This function is supported by System V. It does the same thing as TIOCFLUSH, but the seman-
tics are different.
Session control
TIOCGPGRP
The call ioctl (fd, TIOCGPGRP, pid_t *tpgrp) sets *tpgrp to the ID of the current
process group with which the terminal is associated. 4.4BSD uses this call to implement the
function tcgetpgrp.
TIOCSPGRP
The call ioctl (fd, TIOCSPGRP, pid_t *tpgrp) associates the terminal with the
process group tpgrp. 4.4BSD uses this call to implement the function tcsetpgrp.
TIOCSCTTY
TIOCSCTTY makes the terminal the controlling terminal for the process. This function is sup-
ported by BSD and SunOS systems. On BSD systems, the call is ioctl (fd, TIOCSCTTY,
NULL) and on SunOS systems it is ioctl (fd, TIOCSCTTY, int type). Normally the
controlling terminal will be set only if no other process already owns it. In those implementa-
tions that support type the superuser can set type to 1 in order to force the takeover of the
terminal, even if another process owns it. In 4.4BSD, you would first use the revoke system
call (see Chapter 14, File systems, page 213) to force a close of all file descriptors associated
with the file.
System V and older versions of BSD have no equivalent of this function. In these systems,
when a process group leader without a controlling terminal opens a terminal, it automatically
becomes the controlling terminal. There are methods to ovverride this behaviour: in System
V, you set the flag O_NOCTTY when you open ther terminal. In old BSD versions, you subse-
quently release the control of the terminal with the TIOCNOTTY request, which well look at in
the next section.
TIOCNOTTY
Traditionally, the first time a process without a controlling terminal opened a terminal, it
acquired that terminal as its controlling terminal. We saw in the section on TIOCSCTTY above
that this is no longer the default behaviour in BSD, and that you can override it in System V.
Older BSD versions, including SunOS, did not offer either of these choices. Instead, you had
to accept that you acquired a controlling terminal, and then release the controlling terminal
again with ioctl TIOCNOTTY. If you find this code in a package, and your system doesnt sup-
port it, you can eliminate it. If your system is based on System V, you should check the call to
open for the terminal and ensure that the flag O_NOCTTY is set.
A second use for TIOCNOTTY was after a fork, when the child might want to relinquish the
controlling terminal. This can also be done with setsid (see Chapter 12, Kernel dependen-
cies, page 171).
TIOCGSID
The call ioctl (fd, TIOCGSID, pid_t *pid) stores the terminals session ID at pid.
This function is supported by System V.4.
Miscellaneous functions
TIOCEXCL
The call ioctl (fd, TIOCEXCL, NULL) sets exclusive use on the terminal. No further
opens are permitted except by root.
TIOCNXCL
The call ioctl (fd, TIOCNXCL, NULL) clears exclusive use of the terminal (see TIO-
CEXCL). Further opens are permitted.
TIOCCONS
The call ioctl (fd, TIOCCONS, int *on) sets the console file. If on points to a non-zero
integer, kernel console output is redirected to the terminal specified in the call. If on points to
zero, kernel console output is redirected to the standard console. This is usually used on work
stations to redirect kernel messages to a particular window.
TIOCGSOFTCAR
The call ioctl (fd, TIOCGSOFTCAR, int *set) sets *set to 1 if the terminal Data car-
rier detect (DCD) signal or the software carrier flag is asserted, and to 0 otherwise. This
function is supported only in SunOS 4.X, and is no longer present in Solaris 2. See page 239
for a description of the DSR line.
TIOCSSOFTCAR
The call ioctl (fd, TIOCSSOFTCAR, int *set) is a method to fake a modem carrier
detect signal. It resets software carrier mode if *set is zero and sets it otherwise. In software
carrier mode, the TIOCGSOFTCAR call always returns 1; otherwise it returns the real value of
the DCD interface signal. This function is supported only in SunOS 4.X, and is no longer
present in Solaris 2.
termios functions
It should come as no surprise that people have long wanted a less bewildering interface to ter-
minals than the ioctl calls that we looked at in the previous section. In POSIX.1, a number
of new functions were introduced with the intent of bringing some sort of order into the chaos.
A total of 8 new functions were introduced, split into three groups. In addition, a further 6
auxiliary functions were added:
tcgetattr and tcsetattr get and set terminal attributes using struct termios.
tcgetpgrp and tcsetpgrp get and set the program group ID.
tcdrain, tcflow, tcflush and tcsendbreak manipulate the terminal hardware.
cfgetispeed, cfsetispeed, cfgetospeed, cfsetospeed, cfsetspeed and cfmak-
eraw are auxiliary functions to manipulate termios entries.
These functions do not add new functionality, but attempt to provide a more uniform inter-
face. In some systems, they are system calls, whereas in others they are library functions that
build on the ioctl interface. If you are porting a package that uses termios, and your sys-
tem doesnt supply it, you have the choice of rewriting the code to use ioctl calls, or you can
use the 4.4BSD library calls supplied in the 4.4BSD Lite distribution
(usr/src/lib/libc/gen/termios.c). In the following sections well look briefly at each function.
tcsetattr
tcgetattr sets the current termios state from term.
#include <termios.h>
int tcsetattr (int fd, int action, struct termios *t)
Parameter meaning
TCSANOW Change terminal parameters immediately. Corresponds to the ioctl request
TIOCSETA.
TCSADRAIN First drain output, then change the parameters. Used when changing parame-
ters that affect output. Corresponds to the ioctl call TIOCSETAW.
TCSAFLUSH Discard any pending input, drain output, then change the parameters. Corre-
sponds to ioctl call TIOCSETAF.
tcgetpgrp
tcgetpgrp returns the ID of the current process group with which the terminal is associated.
It corresponds to the ioctl call TIOCGPGRP described on page 263.
#include <sys/types.h>
#include <unistd.h>
pid_t tcgetpgrp (int fd);
tcsetpgrp
tcsetpgrp associates the terminal with the process group tpgrp. It corresponds to the
ioctl call TIOCSPGRP described on page 263.
#include <sys/types.h>
#include <unistd.h>
int tcsetpgrp (int fd, pid_t pgrp_id);
tcdrain
tcdrain suspends the process until all output is drained. It corresponds to the ioctl call
TIOCDRAIN described on page 262.
#include <termios.h>
int tcdrain (int fd);
tcflow
tcflow specifies flow control. It corresponds to the ioctl call TCXONC. See the description
of TCXONC on page 262 for the meaning of the parameter action.
#include <termios.h>
int tcflow (int fd, int action);
tcflush
tcflush flushes input or output queues for fd.
#include <termios.h>
int tcflush (int fd, int action);
Parameter meaning
TCIFLUSH Flush data received but not read
TCOFLUSH Flush data written but not transmitted
TCIOFLUSH Flush both data received but not read and data written but not transmitted
This function corresponds to the ioctl request TCFLSH described on page 263.
tcsendbreak
tcsendbreak sends a break indication on the line. This is equivalent to the ioctl request
TCSBRK described on page 261.
#include <termios.h>
int tcsendbreak (int fd, int len);
Seventh Edition* (see page 240), which allows the field to be stored in 4 bits. They are
located in the field c_cflag. This is not a good idea, because these speeds are the only ones
System V knows about. If you have a V.32bis, V.42bis modem that claims to be able to trans-
fer data at up to 57,600 bps, you will not be able to take full advantage of its capabilities with
System V. In addition, there is only one speed constant, which sets both the input and output
speeds. The functions for setting input and output speed are effectively the same thing.
In addition to these problems, SCO UNIX System V.3 further complicates the issue by provid-
ing the fields s_ospeed and s_ispeed in the struct termios. The functions
cfsetispeed and cfsetospeed set these fields in addition to the four bits in c_cflag, but
the functions cfgetispeed and cfgetospeed retrieve the values from c_cflags, so its not
clear what use the fields c_ispeed and c_ospeed are intended to be.
Setting the bit rates is thus not quite as simple as it might appear: the preprocessor variables
B9600 and friends might not equate to the kind of constant that the termios implementation
needs, and there is no designated place in the termios structure to store the bit rates.
This problem is solved by the following functions, which are normally macros:
speed_t cfgetispeed (struct termios *t) returns ts input speed in speed_t
format. It is undefined if the speed is not representable as speed_t.
int cfsetispeed (struct termios *t, speed_t speed)sets ts input speed to
the internal representation of speed.
speed_t cfgetospeed (struct termios *t) returns ts output speed in speed_t
format. The result is undefined if the speed is not representable as speed_t.
int cfsetospeed (struct termios *t, speed_t speed) sets ts output speed
to the internal representation of speed.
void cfsetspeed (struct termios *t, speed_t speed) sets both input and
output speed to the internal representation of speed.
void cfmakeraw (struct termios *t) sets the whole structure t to default values.
* These constants were originally the values that were written to the interface hardware to set the speed.
269
Difficult to use
The time functions are not easy to use: to get them to do anything useful requires a lot of
work. Youd think that UNIX would supply a primitive call upon which you could easily
build, but unfortunately there isnt any such call, and the ones that are available do not operate
in an intuitively obvious way. For example, there is no standard function for returning the
current time in a useful format.
Implementations differ
There is no single system call that is supported across all platforms. Functions are imple-
mented as system calls in some systems and as library functions in others. As a result, it
doesnt make sense to maintain our distinction between system calls and library functions
when it comes to timekeeping. In our discussion of the individual functions, well note which
systems implement them as system calls and which as library calls.
struct timeval
{
long tv_sec; /* seconds since Jan. 1, 1970 */
long tv_usec; /* and microseconds */
};
It is used for a number of newer functions, such as gettimeofday and setitimer.
Many library routines represent the calendar time as a struct tm. It is usually defined in
/usr/include/time.h:
struct tm
{
int tm_sec; /* seconds after the minute [0-60] */
int tm_min; /* minutes after the hour [0-59] */
int tm_hour; /* hours since midnight [0-23] */
int tm_mday; /* day of the month [1-31] */
int tm_mon; /* months since January [0-11] */
int tm_year; /* years since 1900 */
int tm_wday; /* days since Sunday [0-6] */
int tm_yday; /* days since January 1 [0-365] */
int tm_isdst; /* Daylight Savings Time flag */
The punctuation varies: this example comes from System V.3, which requires a semi-
colon in the indicated position. Other systems allow a comma here, which works until
you try to move the information to System V.3.
The variable altzone, used in SVR4 and XENIX, specifies the number of minutes that
the Daylight Savings Time zone is west of Greenwich.
The variable daylight, used in SVR4 and XENIX, indicates that Daylight Savings
Time is currently in effect.
The variable tzname, used in BSD, SVR4 and XENIX, is a pointer to two strings, speci-
fying the name of the standard time zone and the Daylight Savings Time zone respec-
tively.
In the following sections well look at how to get the current time, how to set the current time,
how to convert time values, and how to suspend process execution for a period of time.
time
#include <sys/types.h>
#include <time.h>
time returns the current time in time_t form, both as a return value and at tloc if this is not
NULL. time is implemented as a system call in System V and as a library function (which
calls gettimeofday) in BSD. Since it returns a scalar value, a call to time can be used as a
parameter to functions like localtime or ctime.
ftime
ftime is a variant of time that returns time information with a resolution of one millisecond.
It originally came from 4.2BSD, but is now considered obsolete.
#include <sys/types.h>
#include <sys/timeb.h>
struct timeb
{
time_t time; /* the same time returned by time */
unsigned short millitm; /* Milliseconds */
short timezone; /* System default time zone */
short dstflag; /* set during daylight savings time */
};
The timezone returned is the system default, possibly not what you want. System V.4 depre-
cates* the use of this variable as a result. Depending on which parameters are actually used,
there are a number of alternatives to ftime. In many cases, time supplies all you need.
However, time is accurate only to one second.
On some systems, you may be able to define ftime in terms of gettimeofday, which
returns the time of the day with a 1 microsecond resolutionsee the next section. On other
systems, unfortunately, the system clock does not have a finer resolution than one second, and
you are stuck with time.
gettimeofday
#include <sys/time.h>
struct timeval
{
long tv_sec; /* seconds since Jan. 1, 1970 */
long tv_usec; /* and microseconds */
};
gettimeofday returns the current system time, with a resolution of 1 microsecond, to tp.
The name is misleading, since the struct timeval representation does not relate to the time
of day. Many implementations ignore tzp, but others, such as SunOS 4, return time zone
information there.
In BSD, gettimeofday is a system call. In some versions of System V.4 it is emulated as a
library function defined in terms of time, which limits its resolution to 1 second. Other ver-
sions of System V appear to have implemented it as a system call, though this is not docu-
mented.
* The term deprecate is a religious term meaning to seek to avert by prayer. Nowadays used to indi-
cate functionality that the implementors or maintainers wish would go away. This term seems to have
come from Berkeley. To quote the New Hackers Dictionary:
:deprecated: adj. Said of a program or feature that is considered obsolescent and in the
process of being phased out, usually in favor of a specified replacement. Deprecated features
can, unfortunately, linger on for many years. This term appears with distressing frequency in
standards documents when the committees writing the documents realize that large amounts of
extant (and presumably happily working) code depend on the feature(s) that have passed out of
favor. See also {dusty deck}.
adjtime
#include <sys/time.h>
adjtime makes small adjustments to the system time, and is intended to help synchronize
time in a network. The adjustment is made graduallythe system slows down or speeds up
the passage of time by a fraction of a percent until it has made the correction, in order not to
confuse programs like cron which are watching the time. As a result, if you call adjtime
again, the previous adjustment might still not be complete; in this case, the remaining adjust-
ment is returned in olddelta. adjtime was introduced in 4.3BSD and is also supported by
System V. It is implemented as a system call in all systems.
settimeofday
#include <sys/time.h>
settimeofday is a BSD system call that is emulated as a library function in System V.4. It
sets the current system time to the value of tp. The value of tzp is no longer used. In Sys-
tem V, this call is implemented in terms of the stime system call, which sets the time only to
the nearest second. If you really need to set the time more accurately in System V.4, you can
use adjtime.
stime
#include <unistd.h>
stime sets the system time and date. This is the original Seventh Edition function that is still
available in System V. It is not supported in BSDuse settimeofday instead on BSD sys-
tems.
appended to their names. These functions use a user-supplied buffer to store the data they
return.
strftime
#include <sys/types.h>
#include <time.h>
#include <string.h>
size_t strftime (char *s, size_t maxsize, char *format, struct tm *tm);
strftime converts the time at tm into a formatted string at s. format specifies the format of
the resultant string, which can be no longer than maxsize characters. format is similar to
the format strings used by printf, but contains strings related to dates. strftime has a
rather strange return value: if the complete date string, including the terminating NUL charac-
ter, fits into the space provided, it returns the length of the stringotherwise it returns 0,
which implies that the date string has been truncated.
strftime is available on all platforms and is implemented as a library function. System V.4
considers ascftime and cftime to be obsolete. The man pages state that strftime should
be used instead.
strptime
#include <time.h>
strptime is a library function supplied with SunOS 4. It converts the date and time string
buf into a struct tm value at tm. This call bears the same relationship to scanf that strf-
time bears to printf.
ascftime
#include <sys/types.h>
#include <time.h>
ascftime converts the time at tm into a formatted string at buf. format specifies the format
of the resultant string. This is effectively the same function as strftime, except that there is
no provision to supply the maximum length of buf. ascftime is available on all platforms
and is implemented as a library function.
asctime converts a time in struct tm* format into the same kind of string that is returned
by ctime. asctime is available on all platforms and is always a library function.
asctime_r is a version of asctime that returns the string to the user-provided buffer res,
which must be at least buflen characters long. It returns the address of res. It is supplied as
a library function on Solaris systems.
cftime
#include <sys/types.h>
#include <time.h>
cftime converts the time at clock into a formatted string at buf. format specifies the for-
mat of the resultant string. This is effectively the same function as strftime, except that
there is no provision to supply the maximum length of buf, and the time is supplied in
time_t format. cftime is available on all platforms and is implemented as a library func-
tion.
ctime converts the time clock into a string in the form Sat Sep 17 14:28:03 1994\n,
which has the advantage of consistency: it is not a normal representation anywhere in the
world, and immediately brands any printed output with the word UNIX. It uses the environ-
ment variable TZ to determine the current time zone. You can rely on the string to be exactly
26 characters long, including the final \0, and to contain that irritating \n at the end. ctime
is available on all platforms and is always a library function.
ctime_r is a version of ctime that returns its result in the buffer pointed to by buf. The
length is limited to buflen bytes. ctime_r is available on Solaris platforms as a library
function.
dysize
#include <time.h>
dysize return the number of days in year. It is supplied as a library function in SunOS 4.
gmtime converts a time in time_t format into struct tm* format, like localtime. As the
name suggests, however, it does not account for local timezonesit returns a UTC time (this
was formerly called Greenwich Mean Time, thus the name of the function). gmtime is avail-
able on all platforms and is always a library function.
gmtime_r is a version of gmtime that returns the string to the user-provided buffer res. It
returns the address of res. It is supplied as a library function on Solaris systems.
localtime converts a time in time_t format into struct tm* format. Like ctime, it uses
the time zone information in tzname to convert to local time. localtime is available on all
platforms and is always a library function.
localtime_r is a version of localtime that returns the string to the user-provided buffer
res. It returns the address of res. It is supplied as a library function on Solaris systems.
mktime
#include <sys/types.h>
#include <time.h>
time_t mktime (struct tm *tm);
mktime converts a local time in struct tm format into a time in time_t format. It does not
use tzname in the conversion it uses the information at tm->tm_zone instead. In addition
to converting the time, mktime also sets the members wday (day of week) and yday (day of
year) of the input struct tm to agree with day, month and year. tm->tm_isdst determines
whether Daylight Savings Time is applicable:
if it is > 0, mktime assumes Daylight Savings Time is in effect.
If it is 0, it assumes that no Daylight Savings Time is in effect.
If it is < 0, mktime tries to determine whether Daylight Savings Time is in effect or not.
It is often wrong.
mktime is available on all platforms and is always a library function.
timegm
#include <time.h>
timegm converts a struct tm time, assumed to be UTC, to the corresponding time_t value.
This is effectively the same thing as mktime with the time zone set to UTC, and is the con-
verse of gmtime. timegm is a library function supplied with SunOS 4.
timelocal
#include <time.h>
difftime
#include <sys/types.h>
#include <time.h>
difftime returns the difference in seconds between two time_t values. This is effectively
the same thing as (int) time1 - (int) time0. difftime is a library function available
on all platforms.
timezone
#include <time.h>
timezone returns the name of the timezone that is zone minutes west of Greenwich. If dst
is non-0, the name of the Daylight Savings Time zone is returned instead. This call is obso-
lete it was used at a time when time zone information was stored as the number of minutes
west of Greenwich. Nowadays the information is stored with a time zone name, so there
should be no need for this function.
tzset
#include <time.h>
tzset sets the value of the internal variables used by localtime to the values specified in
the environment variable TZ. It is called by asctime. In System V, it sets the value of the
global variable daylight. tzset is a library function supplied with BSD and System V.4.
tzsetwall
#include <time.h>
tzsetwall sets the value of the internal variables used by localtime to the default values
for the site. tzsetwall is a library function supplied with BSD and System V.4.
nap
nap is a XENIX variant of sleep with finer resolution:
#include <time.h>
nap suspends process execution for at least millisecs milliseconds. In practice, the XENIX
clock counts in intervals of 20 ms, so this is the maximum accuracy with which you can spec-
ify millisecs. You can simulate this function with usleep (see page 281 for more details).
setitimer
BSD systems and derivatives maintain three (possibly four) interval timers:
A real time timer, ITIMER_REAL, which keeps track of real elapsed time.
A virtual timer, ITIMER_VIRTUAL, which keeps track of process execution time, in other
words the amount of CPU time that the process has used.
A profiler timer, ITIMER_PROF, which keeps track of both process execution time and
time spent in the kernel on behalf of the process. As the name suggests, it is used to
implement profiling tools.
A real time profiler timer, ITIMER_REALPROF, used for profiling Solaris 2.X multi-
threaded processes.
These timers are manipulated with the system calls getitimer and setitimer:
#include <sys/time.h>
struct timeval
{
long tv_sec; /* seconds */
long tv_usec; /* and microseconds */
};
struct itimerval
{
struct timeval it_interval; /* timer interval */
struct timeval it_value; /* current value */
};
setitimer sets the value of a specific timer which to value, and optionally returns the pre-
vious value in ovalue if this is not a NULL pointer. getitimer just returns the current value
of the timer to value. The resolution is specified to an accuracy of 1 microsecond, but it is
really limited to the accuracy of the system clock, which is more typically in the order of 10
milliseconds. In addition, as with all timing functions, there is no guarantee that the process
will be able to run immediately when the timer expires.
In the struct itimerval, it_value is the current value of the timer, which is decremented
depending on type as described above. When it_value is decremented to 0, two things hap-
pen: a signal is generated, and it_value is reloaded from it_interval. If the result is 0,
no further action occurs; otherwise the system continues decrementing the counter. In this
way, one call to setitimer can cause the system to generate continuous signals at a specified
interval.
The signal that is generated depends on the timer. Heres an overview:
Timer Signal
Real time SIGALRM
Virtual SIGVTALRM
Profiler SIGPROF
Real-time profiler1 SIGPROF
1 Only Solaris 2.x
The only timer youre likely to see is the real time timer. If you dont have it, you can fake it
with alarm. In System V.4, setitimer is implemented as a library function that calls an
undocumented system call. See The Magic Garden explained: The Internals of UNIX System
sleep
#include <unistd.h>
The library routine sleep suspends program execution for approximately seconds seconds.
It is available on all UNIX platforms.
usleep
usleep is a variant of sleep that suspends program execution for a very short time:
#include <unistd.h>
void usleep (u_int microseconds);
usleep sleeps for at least microseconds microseconds. It is supplied on BSD and System
V.4 systems as a library function that uses the setitimer system call.
or
void usleep (int microseconds)
{
poll (0, NULL, microseconds / 1000);
}
283
To do a complete job of error checking, ANSI C requires the prototype in the new,
embedded form:
int foo (char *zot, int glarp);
and not
int foo (zot, glarp);
char *zot;
Old C compilers dont understand this new kind of prototype.
Header files usually contain many definitions that are not part of POSIX.1. A mecha-
nism is needed to disable these definitions if you are compiling a program intended to be
POSIX.1 compatible.*
The result of these requirements is spaghetti header files: you frequently see things like this
excerpt from the header file stdio.h in 4.4BSD:
/*
* Functions defined in ANSI C standard.
*/
__BEGIN_DECLS
void clearerr __P((FILE *));
int fclose __P((FILE *));
__END_DECLS
/*
* Functions defined in POSIX 1003.1.
*/
#ifndef _ANSI_SOURCE
#define L_cuserid 9 /* size for cuserid(); UT_NAMESIZE + 1 */
#define L_ctermid 1024 /* size for ctermid(); PATH_MAX */
__BEGIN_DECLS
char *ctermid __P((char *));
__END_DECLS
#endif /* not ANSI */
/*
* Routines that are purely local.
*/
* Writing your programs to conform to POSIX.1 may be a good idea if you want them to run on as
many platforms as possible. On the other hand, it may also be a bad idea: POSIX.1 has very rudimen-
tary facilities in some areas. You may find it more confining than is good for your program.
__END_DECLS
Well, it does look vaguely like C, but this kind of header file scares most people off. A num-
ber of conflicts have led to this kind of code:
The ANSI C library and POSIX.1 carefully define a subset of the total available func-
tionality. If you want to abide strictly to the standards, any extension must be flagged as
an error, even if it would work.
The C++ language has a different syntax from C, but both languages share a common set
of header files.
These solutions have caused new problems, which well examine in this chapter.
First, two underscores are appended to the name of the function. With the initial under-
score we get for the assembler, the name is now _sense__.
Then the class name, Internal is added. Since the length of the name needs to be spec-
ified, this is put in first: _sense__8Internal.
Next, the parameters are encoded. Simple types like int and char are abbreviated to a
single character (in this case, i and c. If they have modifiers like unsigned, these, too,
are encoded and precede the type information. In this case, we get just plain i for the int
parameter, and PUc (a Pointer to Unsigned characters for the second parameter:
_sense__8InternaliPUc.
Class or structure references again cant be coded ahead of time, so again the length of
the name and the name itself is used. In this case, we have a reference, so the letter R is
placed in front of the name: _sense__8InternaliPUcR8Internal.
Finally, the ellipses are specified with the letter e: _sense__8InternaliPUcR8Inter-
nale.
For more details on function name mangling, see The Annotated C++ Reference Manual by
Margaret Ellis and Bjarne Stroustrup.
This difference in naming is a problem when a C++ program really needs to call a function
written in C. The name in the object file is not mangled, and so the C++ compiler must not
output a reference to a mangled name. Theoretically, there could be other differences between
C++ calls and C calls that the compiler also needs to take into account. You cant just assume
that a function written in another language adheres to the same conventions, so you have to
tell it when a called function is written according to C conventions rather than according to
C++ conventions.
This is done with the following elegant construct:
extern "C"
{
char *ctermid (char *);
};
It would be a pain to have a separate set of header files for each version. Instead, the imple-
mentors defined preprocessor variables which evaluate to language constructs for certain
places:
__BEGIN_DECLS is defined as extern C { for C++ and nothing otherwise.
__END_DECLS is defined as }; for C++ and nothing otherwise.
__P(foo) is defined as foo for C++ and ANSI C, and nothing otherwise. This is the
reason why the arguments to __P() are enclosed in double parentheses: the outside level
of parentheses gets stripped by the preprocessor.
#if defined(__cplusplus)
#define __BEGIN_DECLS extern "C" {
#define __END_DECLS };
#else
#define __BEGIN_DECLS
#define __END_DECLS
#endif
This is a common technique introduced by ANSI C: the preprocessor only processes the body
of the header file the first time. After that, the preprocessor variable _CDEFS_H_ is defined,
and the body will not be processed again.
There are a couple of things to note about this method:
There are no hard and fast rules about the naming and definition of these auxiliary vari-
ables. The result is that not all header files use this technique. For example, in FreeBSD
1.1, the header file machine/limits.h defines a preprocessor variable _MACHINE_LIM-
ITS_H and only interprets the body of the file if this preprocessor variable was not set on
entry. BSD/OS 1.1, on the other hand, does not. The same header file is present, and the
text is almost identical, but there is nothing to stop you from including and interpreting
machine/limits.h multiple times. The result can be that a package that compiles just fine
under FreeBSD may fail to compile under BSD/OS.
The ANSI standard defines numerous standard preprocessor variables to ensure that
header files are interpreted only the first time they are included. The variables all start
with a leading _, and the second character is either another _ or an upper-case letter. Its
a good idea to avoid using such symbols in your sources.
We could save including sys/cdefs.h multiple times by checking _CDEFS_H_ before
including it. Unfortunately, this would establish an undesireable relationship between
the two files: if for some reason it becomes necessary to change the name of the pre-
processor variable, or perhaps to give it different semantics (like giving it different values
at different times, instead of just being defined), you have to go through all the header
files that refer to the preprocessor variable and modify them.
Type information
A large number of system and library calls return information which can be represented in a
single machine word. The machine word of the PDP-11, on which the Seventh Edition ran,
was only 16 bits wide, and in some cases you had to squeeze the value to get it in the word.
For example, the Seventh Edition file system represented an inode number in an int, so each
file system could have only 65536 inodes. When 32-bit machines were introduced, people
quickly took the opportunity to extend the length of these fields, and modern file systems such
as ufs or vxfs have 32 bit inode numbers.
These changes were an advantage, but they bore a danger with them: nowadays, you cant be
sure how long an inode number is. Current systems really do have different sized fields for
inode numbers, and this presents a portability problem. Inodes arent the only thing that has
changed: consider the following structure definition, which contains information returned by
system calls:
struct process_info
{
long pid; /* process number */
long start_time; /* time process was started, from time () */
long owner; /* user ID of owner */
long log_file; /* file number of log file */
long log_file_pos; /* current position in log file */
short file_permissions; /* default umask */
short log_file_major; /* major device number for log file */
short log_file_minor; /* minor device number */
short inode; /* inode number of log file */
}
On most modern systems, the longs take up 32 bits and the shorts take up 16 bits. Because
of alignment constraints, we put the longest data types at the front and the shortest at the end
(see Chapter 11, Hardware dependencies, page 158 for more details). And for older systems,
these fields are perfectly adequate. But what happens if we port a program containing this
structure to a 64 bit machine running System V.4 and vxfs? Weve already seen that the inode
numbers are now 32 bits long, and System V.4 major and minor device numbers also take up
more space. If you port this package to 4.4BSD, the field log_file_pos needs to be 64 bits
long.
Clearly, its an oversimplification to assume that any particular kind of value maps to a short
or a long. The correct way to do this is to define a type that describes the value. In modern
C, the structure above becomes:
struct process_info
{
pid_t pid; /* process number */
time_t start_time; /* time process was started, from time () */
uid_t owner; /* user ID of owner */
long log_file; /* file number of log file */
pos_t log_file_pos; /* current position in log file */
mode_t file_permissions; /* default umask */
short log_file_major; /* major device number for log file */
short log_file_minor; /* minor device number */
inode_t inode; /* inode number of log file */
}
Its important to remember that these type definitions are all in the mind of the compiler, and
that they are defined in a header file, which is usually called sys/types.h: the system handles
them as integers of appropriate length. If you define them in this manner, you give the com-
piler an opportunity to catch mistakes and generate more reliable code. Check your man
pages for the types of the arguments on your system if you run into trouble. In addition, Ap-
pendix A, Comparative reference to UNIX data types, contains an overview of the more com-
mon types used in UNIX systems.
/usr/include/sys
In early versions of UNIX, this directory contained the header files used for compiling the
kernel. Nowadays, this directory is intended to contain header files that relate to the UNIX
implementation, though the usage varies considerably. You will frequently find files that
directly include files from /usr/include/sys. In fact, it may come as a surprise that this is not
supposed to be necessary. Often you will also see code like
This simplified example shows what you need to do because System V keeps the header file
err.h in /usr/include/sys, whereas other flavours keep it in /usr/include. In order to include the
file correctly, the source code needs to know what kind of system it is running on. If it
guesses wrong (for example, if USG is not defined when it should be) or if the author of the
package didnt allow for System V, either out of ignorance, or because the package has never
been compiled on System V before, then the compilation will fail with a message about miss-
ing header files.
Frequently, the decisions made by the kind of code in the last example are incorrect. Some
header files in System V have changed between System V.3 and System V.4. If, for example,
you port a program written for System V.4 to System V.3, you may find things like
#include <wait.h>
This will fail in most versions of System V.3, because there is no header file
/usr/include/wait.h; the file is called /usr/include/sys/wait.h. There are a couple of things you
could do here:
You could start the compiler with a supplementary -I/usr/include/sys, which will
cause it to search /usr/include/sys for files specified without any pathname component.
The problem with this approach is that you need to do it for every package that runs into
this problem.
You could consider doing what System V.4 does in many cases: create a file called
/usr/include/wait.h that contains just an obligatory copyright notice and an #include
directive enclosed in #ifdefs:
/* THIS IS PUBLISHED NON-PROPRIETARY SOURCE CODE OF OREILLY */
/* AND ASSOCIATES Inc. */
/* The copyright notice above does not evidence any actual or */
/* intended restriction on the use of this code. */
#ifndef _WAIT_H
#define _WAIT_H
#include <sys/wait.h>
#endif
Incompatible definitions. The definitions are there, but they dont match your compiler.
This is particularly often the case with C++ on systems that dont have a native C++
compiler. The gcc utility program protoize, which is run when installing gcc, is sup-
posed to take care of these differences, and it may be of use even if you choose not to
install gcc.
Incorrect #ifdefs. For example, the file may define certain functions only if
_POSIX_SOURCE is defined, even though _POSIX_SOURCE is intended to restrict func-
tionality, not to enable it. The System V.4.2 version math.h surrounds M_PI (the constant
pi) with
#if (__STDC__ && !defined(_POSIX_SOURCE)) || defined(_XOPEN_SOURCE)
In other words, if you include math.h without defining __STDC__ (ANSI C) or
_XOPEN_SOURCE (X Open compliant), M_PI will not be defined.
The header files may contain syntax errors that the native compiler does not notice, but
which cause other compilers to refuse them. For example, some versions of XENIX
curses.h contain the lines:
#ifdef M_TERMCAP
# include <tcap.h> /* Use: cc -DM_TERMCAP ... -lcurses -ltermlib */
#else
# ifdef M_TERMINFO
# include <tinfo.h> /* Use: cc -DM_TERMINFO ... -ltinfo [-lx] */
# else
ERROR -- Either "M_TERMCAP" or "M_TERMINFO" must be #defined.
# endif
#endif
This does not cause problems for the XENIX C compiler, but gcc, for one, complains
about the unterminated character constant starting with defined.
The header files may be missing. In the course of time, header files have come and
gone, and the definitions have been moved to other places. In particular, the definitions
that used to be in strings.h have been moved to string.h (and changed somewhat on the
way), and termio.h has become termios.h (see Chapter 15, Terminal drivers, page 241 for
more details).
The solutions to these problems are many and varied. They usually leave you feeling dissatis-
fied:
Fix the system header files. This sounds like heresy, but if you have established beyond
any reasonable doubt that the header file is to blame, this is about all you can do, assum-
ing you can convince your system administrator that it is necessary. If you do choose
this way, be sure to consider whether fixing the header file will break some other pro-
gram that relies on the behaviour. In addition, you should report the bugs to your vendor
and remember to re-apply the updates when you install a newer version of the operating
system.
Use the system header files, but add the missing definitions in local header files, or,
worse, in the individual source files. This is a particularly obnoxious solution,
especially when, as so often, the declarations are not dependent on a particular ifdef. In
almost any system with reasonably complete header files there will be discrepancies
between the declarations in the system header files and the declarations in the package.
Even if they are only cosmetic, they will stop an ANSI compiler from compiling. For
example, your system header files may declare getpid to return pid_t, but the package
declares it to return int.
About the only legitimate use of this style of fixing is to declare functions that will
really cause incorrect compilation if you dont declare them. Even then, declare them
only inside an ifdef for a specific operating system. In the case of getpid, youre better
off not declaring it: the compiler will assume the correct return values. Nevertheless, you
will see this surprisingly often in packages that have already been ported to a number of
operating systems, and its one of the most common causes of porting problems.
Make your own copies of the header files and use them instead. This is the worst idea of
all: if anything changes in your systems header files, you will never find out about it. It
also means you cant give your source tree to somebody else: in most systems, the header
files are subject to copyright.
alloca
alloca allocates memory in the stack, as opposed to malloc, which allocates memory in the
heap:
void *alloca (size_t size);
This has a significant speed advantage over malloc: malloc needs to search and update a
free space list. alloca typically just needs to change the value of a register, and is thus very
fast. It is often included with the package you are compiling, but you may need to set a flag or
modify the Makefile in order to include it. Versions for VAX, HP 300, i386 and Tahoe
293
bcopy
bcopy is a BSD function that substantially corresponds to memmove:
#include <string.h>
Unlike memcpy, it is guaranteed to move correctly when the source and destination fields
overlap. If your system has memmove, you can define it as:
#define bcopy(s, d, l) memmove (d, s, l)
bzero
bzero is a BSD function to clear an area of memory to 0. It is a subset of the standard func-
tion memset, and you can define it as
#define bzero(d, l) memset (d, \0, l)
fnmatch
fnmatch is a routine that matches patterns according to the shell file name rules:
#include <fnmatch.h>
int fnmatch (const char *pattern, const char *string, int flags);
fnmatch compares string against pattern. It returns 0 if string matches pattern and
FNM_NOMATCH otherwise. The flags in Table 18-1 specify the kind of match to perform:
Flag Meaning
FNM_NOESCAPE Interpret the backslash character (\) literally.
FNM_PATHNAME Slash characters in string must be explicitly matched by slashes in pat-
tern.
FNM_PERIOD Leading periods in strings match periods in patterns. Not all versions of
fnmatch implement this flag.
fnmatch is supplied with later BSD versions only. If you need it, it is in the 4.4BSD Lite dis-
tributio as lib/libc/gen/fnmatch.c.
getwd has the great disadvantage that the function does not know the length of the pathname,
and so it can write beyond the end of the buffer. As a result, it has been replaced by getcwd,
which specifies a maximum length for the returned string. You can define getwd as:
#define getwd(d) getcwd (d, MAXPATHLEN)
MAXPATHLEN is a kernel constant defining the maximum path name length. It is normally
defined in /usr/include/sys/param.h.
gethostname returns a null-terminated string if the space defined by namelen allows. This
function is supported in System V.4, but not in standard versions of System V.3 and XENIX.
On System V systems, the system call uname returns a number of items of information about
the system, including the name. It is also supported as a library function by most BSD sys-
tems.
#include <sys/utsname.h>
sys/utsname.h defines
struct utsname
{
char sysname [9]; /* Internal system name */
char nodename [9]; /* External system name */
char release [9]; /* Operating system release */
char version [9]; /* Version of release */
char machine [9]; /* Processor architecture */
};
int uname (struct utsname *name);
The systems that do support uname apply a different meaning to the field sysname. For
example, consider the output of the following program, which was compiled and run on Inter-
active UNIX/386 System V.3 Version 2.2 and BSD/386 version 1.1, both running on an Intel
486 platform:
#include <sys/utsname.h>
main ()
{
struct utsname myname;
uname (&myname);
printf ("sysname %s nodename %s release %s version %s machine %s\n",
myname.sysname,
myname.nodename,
myname.release,
myname.version,
myname.machine);
}
$ uname On the System V.3 machine:
sysname adagio nodename adagio release 3.2 version 2 machine i386
$ uname On the BSD/386 machine:
sysname BSD/386 nodename allegro release 1.1 version 0 machine i386
System V puts the node name in sysname, whereas BSD uses it for the name of the operating
system. This information is by no means complete: in particular, neither version tells you
explicitly whether the system is running System V or BSD, and there is no indication of the
vendor at all on the System V system.
index
index searches the string s forwards for the first occurrence of the character c. If it finds
one, it returns a pointer to the character. Otherwise it returns NULL. It is essentially the same
as the ANSI function strchr, and you can define it as:
#define index strchr
malloc
malloc has always been around, but the semantics have changed in the course of time. In the
Seventh Edition and XENIX, a call to malloc with length 0 returned a valid pointer, whereas
later versions return a NULL pointer, indicating an error. As a result, programs that ran on
older versions might fail on more recent implementations.
memmove
memmove copies an area of memory:
#include <string.h>
This is the same function as memcpy, except that memmove is guaranteed to move overlapping
data correctly. Except for the parameter sequence, it is the same as bcopy (see page 294). If
you dont have either function, you can find bcopy in the 4.4BSD library source
remove
#include <stdio.h>
On BSD systems, remove is a synonym for the system call unlink. This means that it makes
sense to use it only for files. On System V.4 systems, it is slightly more complicated: if called
for a file, it does the same thing as unlink, for directories it does the same thing as rmdir.
rindex
rindex (reverse index) searches the string s for the last occurrence of character c and returns
a pointer to the character if it is found, and NULL otherwise. It is essentially the same function
as strrchr, and you can define it as:
#define rindex strrchr
The argument size specifies the maximum length of the output string, including the trailing
\0. These functions are supplied in 4.4BSD Lite as usr/src/lib/libc/stdio/snprintf.c and
usr/src/lib/libc/stdio/vsnprintf.c. Alternatively, you can remove the second parameter and use
sprintf or vsprintf instead.
#include <string.h>
strncasecmp differs from strcasecmp by comparing at most len characters. Both func-
tions stop comparing when a NUL character is detected in one of the strings. You can find both
functions in the 4.4BSD Lite distribution (lib/libc/string/strcasecmp.c).
strdup
strdup allocates memory with malloc and copies a string to it:
#include <string.h>
errnum is the number of the error; strerror returns a pointer to a text for the error, or
NULL if none is found.
Most library implementations also define sys_errlist, an array of description strings for
errors, and sys_nerr, the total number of error messages, in other words, the number of mes-
sages in sys_errlist. If you dont find this function anywhere in your man pages, dont
give up: its frequently hidden in an unexpected library. For example, NonStop UX version
B22 doesnt define or document sys_errlist anywhere, but it is in libc.a all the same.
The implementation of strerror is trivial:
char *strerror (int errnum)
{
if (errnum < sys_nerr)
return sys_errlist [errnum];
else
{
static char bogus [80];
sprintf (bogus, "Unknown error: %d", errnum);
return bogus;
}
}
Dont assume that your system doesnt have sys_errlist just because you cant find a
definition in the header files. Many systems install it via the back door because packages such
as X11 use them. The safest way to find out is to search the system libraries. The shell script
findf, described in Chapter 21, Object files and friends, page 374, will do this for you.
On an Intel 386 architecture, gcc compiles quite a tight little loop with only 7 instructions,*
but it also requires another 15 instructions to set up the function environment and remove it
again. In addition, the calling sequence memcpy (bar, foo, 10) might compile in 5
instructions. Many machines supply special instructions for block memory operations, but
even those that dont can do it faster without a function call. The block memory functions are
thus ideal candidates for inline functions that the compiler can optimize. Many compilers
now do so, including gcc and the System V.4 CCS compilers. In this situation, the compiler
can recognize that there are only a few bytes to be moved, and that they are word-aligned, so
it can use native load and store operations. When you enable optimization, gcc can compiles
the memcpy (bar, foo, 10) into only 6 simple instructions: the loop has disappeared com-
pletely, and we just have 3 load and 3 store instructions. This approach isnt appropriate for
moves of 1000 bytes, of course. Here, the compiler uses 4 instructions to set up a block move
instruction, for a total of 5 instructions.
These examples are typical of what a smart compiler can do if it has inside information about
what the function does. Normally this information is compiled into the compiler, and it
doesnt need a header file to know about the function. This can have a number of conse-
quences for you:
The compiler knows the parameter types for the function. If you define the function
differently, you get a possibly confusing error message:
* See Chapter 21, Object files and friends, page 377 for a discussion of parameter passing.
* ed does have its uses, though. If you have serious system problems (like /usr crashed), its nice to have
a copy of ed on the root file system. Its also useful when your only connection to the machine is via a
slow modem line: over a 2400 bps line, redrawing a 24x80 screen with vi or emacs takes 8 seconds, and
things are a lot faster with ed.
Apart from the intention of the functions, they also perform their task in very different ways.
They might store compiled program in an area that the caller supplies, they might malloc it, or
they might hide it where the user cant find it. In some cases, the compiled expression is
stored in a struct along with other information intended for use by the calling functions. In
others this information is not returned at all, and in others again it is returned in global arrays,
and in others it is returned via optional arguments. Translating from one flavour to another
takes a lot of time. Three packages are generally available: Henry Spencers Eighth Edition
package, the 4.4BSD POSIX.2 version, and the GNU POSIX.2 version. See Appendix E,
Where to get sources for sources of these packages. If you do have to port to a different regu-
lar expression package, choose a POSIX.2 implementation. Although it is by far the most
complicated to understand, it is probably the only one that will be around in a few years.
Regular expressions are used for two purposes: searching and replacing. When replacing one
regular expression with another, its nice to be able to refer to parts of the expression. By con-
vention, you define these subexpressions with parentheses: the expression
foo\(.*\)bar\(.*\)baz defines two such subexpressions. The regular expression will
match all strings containing the texts foo, bar, and baz with anything in between them. The
first marked subexpression is the text between foo and bar, and the second one is the text
between bar and baz.
regexpr
The regexpr routines have traditionally been supplied with System V and XENIX systems.
They were originally part of the ed editor and thus implement the ed style of regular expres-
sions. Despite the name, there is no function called regexpr.
The routines themselves are normally in a library libgen.a. In addition, for some reason many
versions of System V and XENIX include the complete source to the functions in the header
file regexp.h, whereas the header file regexpr.h contains only the declarations. There are three
routines:
#include <regexpr.h>
compile compiles the regular expression instring. The exact behaviour depends on
the value of expbuf. If expbuf is NULL, compile mallocs space for the compiled ver-
sion of the expression and returns a pointer to it. If expbuf is non-NULL, compile
places the compiled form there if it fits between expbuf and endbuf, and it returns a
pointer to the first free byte. If the regular expression does not fit in this space, compile
aborts. If the compilation succeeds, the length of the compiled expression is stored in the
Error Meaning
code
11 Range endpoint too large.
16 Bad number.
25 \digit out of range.
36 Illegal or missing delimiter.
41 No remembered search string.
42 \( \) imbalance.
43 Too many \(.
44 More than 2 numbers given in \{ \}.
45 } expected after \.
46 First number exceeds second in \{ \}.
49 [ ] imbalance.
50 Regular expression overflow.
step compares the string string with the compiled expression at expbuf. It returns
non-zero if it finds a match, and 0 if it does not. If there is a match, the pointer loc1 is
set to point to the first matching character, and loc2 is set to point past the last character
of the match.
If the regular expression contains subexpressions, expressions bracketed by the character
sequences \( and \), step stores the locations of the start and end of each matching
string in the global arrays braslist (start) and braelist (end). It stores the total num-
ber of such subexpressions in nbra.
advance has the same function as step, but it restricts its matching to the beginning of
the string. In other words, a match always causes loc1 to point to the beginning of the
string.
regcmp
regcmp is another regular expression processor used in System V and XENIX. Like regexpr,
they implement the ed style of regular expressions with some extensions. They are also nor-
mally part of the library libgen.a.
#include <libgen.h>
regcmp can take multiple input arguments, which it concatenates before compilation.
This can be useful when the expression is supplied on a number of input lines, for exam-
ple. It always mallocs space for its compiled expression, and returns a pointer to it.
regex searches for the string subject in the compiled regular expression re. On suc-
cess, it returns a pointer to the next unmatched character and sets the global pointer
__loc1 to the address of the first character of the match. Optionally, it returns up to ten
strings at ret0 and the parameters that follow. You specify them with the $n regular
expression element discussed below.
The regular expression syntax is slightly different from that of ed and sed:
The character $ represents the end of the string, not the end of the line. Use \n to spec-
ify the end of the line.
You can use the syntax [a-f] to represent [abcdef].
You can use the syntax x+ to represent one or more occurrences of x.
You can use the syntax {m}, where m is an integer, to represent that the previous subex-
pression should be applied m times.
You can use the syntax {m,}, where m is an integer, to represent that the previous subex-
pression should be applied at least m times.
You can use the syntax {m,u}, where m and u are integers, to represent that the previous
subexpression should be applied at least m and at most u times.
The syntax (exp) groups the characters exp so that operators such as * and + work on
the whole expression and not just the preceding character. For example, abcabcabcabc
matches the regular expression (abc)+, and abcccccc matches abc+.
The syntax (exp)$n, where n is an integer, matches the expression exp, and returns the
address of the matched string to the call parameter retn of the call to regex. It will
even try to return it if you didnt supply parameter retn, so its good practice to supply
all the parameters unless you are in control of the regular expressions.
re_comp compiles the regular expression sp and stores the compiled form internally.
On successful compilation, it returns a NULL pointer, and on error a pointer to an error
message.
re_exec searches the string p1 against the internally stored regular expression. It
returns 1 if the string p1 matches the last compiled regular expression, 0 if the string p1
fails to match the last compiled regular expression, and -1 if the compiled regular expres-
sion is invalid.
No public-domain versions of regex are available, but its relatively simple to define them in
terms of POSIX.2 regex.
In contrast to earlier packages, Eighth edition regexp implements egrep-style regular expres-
sions. Also in contrast to other packages, the compiled form of the regular expression
includes two arrays which regexec sets for the convenience of the programmer: char
*startp [] is an array of start addresses of up to nine subexpressions (expressions enclosed
in parentheses), and char *endp [] is an array of the corresponding end addresses. The
subexpressions are indexed 1 to 9; startp [0] refers to the complete expression.
regcomp compiles the regular expression exp and stores the compiled version in an area that
it mallocs. It returns a pointer to the compiled version on success or NULL on failure.
regexec matches the string string against the compiled regular expression prog. It returns
1 if a match was found and 0 otherwise. In addition, it stores the start and end addresses of
the first ten parenthesized subexpressions in prog->startp and prog->endp.
regsub performs a regular expression substitution, a function not offered by the other pack-
ages. You use it after regcomp finds a match and stores subexpression start and end informa-
tion in startp and endp. It copies the input string source to dest, replacing expressions of
the type &n, where n is a single digit, by the substring defined by startp [n] and endp
[n].
regerror determines the action to be taken if an error is detected in regcomp, regexec or
regsub. The default regerror prints the message and terminates the program. If you want,
you can replace it with your own routine.
POSIX.2 regex
As if there werent enough regular expression libraries already, POSIX.2 also has a version of
regex. It is intended to put an end to the myriad other flavours of regular expressions, and thus
supplies all the functionality of the other packages. Unfortunately, it re-uses the function
names of Eighth Edition regexp. This is the only similarity: the POSIX.2 functions take com-
pletely different parameters. The header file of the 4.4BSD package starts with
#ifndef _REGEX_H_
#define _REGEX_H_ /* never again */
#ifdef _REGEXP_H_
BAD NEWS -- POSIX regex.h and V8 regexp.h are incompatible
#endif
The Eighth Edition regexp.h contains similar code, so if you accidentally try to use both, you
get an error message when you try to compile it.
The POSIX.2 regex package offers a seemingly infinite variety of flags and options. It con-
sists of the functions regcomp, regexec, regerror and regfree. They match regular
expressions according to the POSIX.2 regular expression format, either ed format (so-called
basic regular expressions, the default) or egrep format (extended regular expressions). The
4.4BSD implementations refer to them as obsolete regular expressions and modern regular
expressions respectively. You choose the kind of expression via flag bits passed to the compi-
lation function regcomp. Alternatively, you can specify that the string is to be matched
exactly no characters have any special significance any more.
Here are the functions:
#include <sys/types.h>
#include <regex.h>
regcomp compiles the regular expression pattern and stores the result at preg. It
returns 0 on success and an error code on failure. cflags modifies the way in which the
regexec matches the string string against the compiled regular expression preg. If
nmatch is non-zero and pmatch is non-NULL, start and end information for up to
nmatch subexpressions is returned to the array pmatch. regexec also supports a num-
ber of flags in eflags. They are described in Table 18-4:
regerror is analogous to the C library function perror: it converts the error code
errcode for regular expression preg into a human-readable error message at errbuf,
up to a maximum of errbuf_size bytes.
As in Eighth Edition regexp, regcomp returns additional information about the expression:
If you compile the expression with the REG_PEND bit set in cflags, you can set
re_endp to point to the real end of the regular expression string supplied to regcomp,
thus allowing NUL characters in the regular expression.
After compilation, regcomp sets re_nsub to the number of subexpressions it found. For
each subexpression, regexec can return start and end address information if you call it
with appropriate parameters.
In addition, regexec stores information about matched subexpressions in a structure of type
regmatch_t, unless you specify the flag REG_NOSUB. This contains at least the fields rm_so
and rm_eo, which are offsets from the start of the string of the first and last character of the
match. They are of type regmatch_t.
No less than three versions of POSIX.2 regex are generally available: Henry Spencers regex is
included in the 4.4BSD distribution, and the GNU project has the older regex and newer rx.
See Appendix E, Where to get sources.
like a can of worms: avoid opening it if you can, otherwise refer to UNIX Curses
Explained by Berny Goodheart. If you have a BSD system and need System V curses,
you can try the ncurses package (see Appendix E, Where to get sources).
BSD versions of UNIX have still not incorporated terminfo or the additional curses rou-
tines, although they have been available for some time. In this section, well look at the
differences in the implementations and what can be done about them. For more informa-
tion, read Programming with curses, by John Strang, for a description of BSD curses,
Termcap and Terminfo, by John Strang, Tim OReilly and Linda Mui for a description of
termcap and terminfo, and UNIX Curses Explained, by Berny Goodheart, for a descrip-
tion of both versions of curses.
termcap
The heart of termcap is the terminal description. This may be specified with an environment
variable, or it may be stored in a file containing definitions for a number of terminals. There
is no complete agreement on the location of this file:
If the termcap routines find the environment variable TERMCAP, and it doesnt start with a
slash (/), they try to interpret it as a termcap entry.
If the termcap routines find the environment variable TERMCAP, and it does start with a
slash, they try to interpret it as the name of the termcap file.
If TERMCAP isnt specified, the location depends on a constant which is compiled into the
termcap library. Typical directories are /etc, /usr/lib, and /usr/share. Dont rely on find-
ing only one of these files: its not uncommon to find multiple copies on a machine, only
one of which is of any use to you. If the system documentation forgets to tell you where
the termcap file is located, you can usually find out with the aid of strings and grep. For
example, BSD/OS gives
$ strings libtermcap.a |grep /termcap
.termcap /usr/share/misc/termcap
and SCO UNIX gives
$ strings libtermcap.a |grep /termcap
/etc/termcap
/etc/termcap
The file termcap contains entries for all terminals known to the system. On some systems it
can be up to 200 kB in length. This may not seem very long, but every program that uses
termcap must read it until it finds the terminal definition: if the terminal isnt defined, it reads
the full length of the file.
Heres a typical entry:
vs|xterm|vs100|xterm terminal emulator (X Window System):\
:AL=\E[%dL:DC=\E[%dP:DL=\E[%dM:DO=\E[%dB:IC=\E[%d@:UP=\E[%dA:\
:al=\E[L:am:\
:bs:cd=\E[J:ce=\E[K:cl=\E[H\E[2J:cm=\E[%i%d;%dH:co#80:\
:cs=\E[%i%d;%dr:ct=\E[3k:\
:dc=\E[P:dl=\E[M:\
:im=\E[4h:ei=\E[4l:mi:\
:ho=\E[H:\
:is=\E[r\E[m\E[2J\E[H\E[?7h\E[?1;3;4;6l\E[4l:\
:rs=\E[r\E[m\E[2J\E[H\E[?7h\E[?1;3;4;6l\E[4l\E<:\
:k1=\EOP:k2=\EOQ:k3=\EOR:k4=\EOS:kb=H:kd=\EOB:ke=\E[?1l\E>:\
:kl=\EOD:km:kn#4:kr=\EOC:ks=\E[?1h\E=:ku=\EOA:\
:li#65:md=\E[1m:me=\E[m:mr=\E[7m:ms:nd=\E[C:pt:\
:sc=\E7:rc=\E8:sf=\n:so=\E[7m:se=\E[m:sr=\EM:\
:te=\E[2J\E[?47l\E8:ti=\E7\E[?47h:\
:up=\E[A:us=\E[4m:ue=\E[m:xn:
v2|xterms|vs100s|xterm terminal emulator, small window (X Window System):\
:co#80:li#24:tc=xterm:
vb|xterm-bold|xterm with bold instead of underline:\
:us=\E[1m:tc=xterm:
#
# vi may work better with this termcap, because vi
# doesnt use insert mode much
vi|xterm-ic|xterm-vi|xterm with insert character instead of insert mode:\
:im=:ei=:mi@:ic=\E[@:tc=xterm:
The lines starting with the hash mark (#) are comments. The other lines are terminal capabil-
ity definitions: each entry is logically on a single line, and the lines are continued with the
standard convention of terminating them with a backslash (\). As in many other old UNIX
files, fields are separated by colons (:).
The first field of each description is the label, the name of the terminal to which the definition
applies. The terminal may have multiple names, separated by vertical bars (|). In our exam-
ple, the first entry has the names vs, xterm and vs100. The last part of the name field is a
description of the kind of terminal. In the second entry, the names are vi, xterm-ic and
xterm-vi.
Both 4.4BSD termcap and System V terminfo recommend the following conventions for nam-
ing terminals:
Start with the name of the physical hardware, for example, hp2621.
Avoid hyphens in the name.
Describe operational modes or configuration preferences for the terminal with an indica-
tor, separated by a hyphen. Use the following suffixes where possible:
Suffix Meaning Example
-w Wide mode (more than 80 columns) vt100-w
-am With automatic margins (usually default) vt100-am
-nam Without automatic margins vt100-nam
-n Number of lines on screen aaa-60
-na No arrow keys (leave them in local) concept100-na
-np Number of pages of memory concept100-4p
-rv Reverse video concept100-rv
The following fields describe individual capabilities in the form capability=definition. The
capabilities are abbreviated to two characters, and case is significant. See Programming with
curses, by John Strang, for a list of the currently defined abbreviations and their meaning.
Depending on the capability, the definition may be a truth value (true or false), a number, or a
string. For example,
The first entry for vs (AL=\E[%dL) states that the capability AL (insert n new blank lines)
can be invoked with the string \E[%dL. \E represents an ESC character. The characters
[ and L are used literally. The program uses sprintf to replace the %d with the number
of lines to insert.
The next entry, am, has no parameter. This is a boolean or truth value, in this case mean-
ing that the terminal supports automatic margins. The presence of the capability means
that it is true, the absence means that it is false.
The entry co#80 specifies a numeric parameter and states that the capability co (number
of columns) is 80.
There is almost nothing in the syntax of the definition file that requires that a particular capa-
bility have a particular type, or even which capabilities exist: this is simply a matter of agree-
ment between the program and the capabilities database: if your program wants the capability
co, and wants it to be numeric, it calls tgetnum. For example, the following code checks first
the information supplied by ioctl TIOCGWINSZ (see Chapter 15, Terminal drivers, page
259), then the termcap entry, and if both of them are not defined, it defaults to a configurable
constant:
if (! (maxcols = winsize.ws_col)
&& (! (maxcols = tgetnum ("co"))) )
maxcols = MAXCOLS;
The only exception to this rule is the capability tc, which refers to another capability. In the
example above, the entry for vi and friends consists of only 5 entries, but the last one is a tc
entry that refers to the vs entry above.
This lack of hard and fast rules means that termcap is extensible: if you have a terminal that
can change the number of colours which can be displayed at one time, and for some reason
you want to use this feature, you might define a termcap variable XC specifying the string to
output to the terminal to perform this function. The danger here is, of course, that somebody
else might write a program that uses the variable XC for some other purpose.
This is all that tgoto does. It does not actually output anything to the terminal.
tputs writes the string at cp to the screen. This seems unnecessary, since write and
fwrite already do the same thing. The problem is that the output string cp may contain
padding information for serial terminals, and only tputs interprets this information cor-
rectly. affcnt is the number of lines on the screen affected by this output, and outc is
the address of a function that outputs the characters correctly. Often enough, this is
something like putchar.
Missing description
It could still happen that there isnt a description for your terminal in the termcap data base.
This isnt the problem it used to be, since most modern terminals come close to the ANSI
standard. In case of doubt, try ansi or vt100. This is, of course, not a good substitute for
complete documentation for your terminal.
Incomplete description
Its much more likely that you will find a terminal description for your terminal, but its
incomplete. This happens surprisingly often. For example, the xterm definition supplied in
X11R5 has 56 capabilities, and the definition supplied with X11R6 has 85. xterm hasnt
changed significantly between X11R5 and X11R6: the capabilities were just missing from the
entry in X11R5. Frequently youll find that a feature youre looking for, in particular the code
generated by a particular function key, is missing from your termcap entry. If nothing else
helps, you can find out what codes the key generates with od:
$ od -c display stdin in character format
[[11[[12[[13[[14 RETURN
0000000 033 [ 1 1 033 [ 1 2 033 [ 1 3 0000020
033 [ 1 4 \n
0000025
In this example, I pressed the keys F1, F2, F3 and F4 on an xterm: this is what echos on the
first line. od doesnt display anything until its read completes, so I pressed RETURN to
show the text. It shows that the sequences generated are:
033 (ESC, which is represented as \E in termcap entries).
[1 and the number of the function key and a tilde ().
These sequences can then be translated into the termcap capabilities:
k1=\E[11:k2=\E[12:k3=\E[13:k4=\E[14:
Incorrect description
If we look at the previous example more carefully, well notice something strange: these capa-
bilities arent the same as the ones in the example for xterm on page 308. Whats wrong with
this picture? A good question. Both under X11R5 and X11R6, xterm on an Intel architecture
gives you the codes shown above. The codes for F5 to F10 are as shown in the termcap entry,
but the entries for F1 to F4 are just plain wrong. I dont know of any way to generate them
with xterm. This is typical of termcap entries: if you run into trouble, first make sure that your
descriptions are correct.
Obsolete information
Another interesting thing about the xterm example is that it tells you the size of the terminal:
co#80 says that this terminal has 80 columns, and li#65 says that it has 65 lines. This infor-
mation can be an approximation at best, since X11 allows you to resize the window. Most
modern systems supply the SIGWINCH signal, which is delivered to the controlling process
when a window is resized (see Chapter 15, Terminal drivers, page 259). This information is
just plain misleading, but theres a lot of it in just about any termcap file. The 4.4BSD man
page flags a number of capabilities that are considered obsolete, including things as the char-
acter used to backspace or the number of function keys.
terminfo
terminfo is the System V replacement for termcap. At first sight it looks very similar, but
there are significant differences:
Instead of reading the termcap file, the terminfo routines require a compiled version.
Instead of storing all entries in a single file, terminfo stores each entry in a different file,
and puts them in a directory whose name is the initial letter of the terminal name. For
example, the terminfo description for xterm might be stored in /usr/lib/terminfo/x/xterm.
The substitution syntax has been significantly expanded: in termcap, only tgoto could
handle parameter substitution (see page 311); in terminfo, the substitution syntax is more
general and offers many more features.
The program tic (terminfo compiler) compiles terminfo descriptions.
The programs infocmp, which is supplied with System V, and untic, which is part of
ncurses, dump compiled terminfo entries in source form.
As an example of a terminfo definition, lets look at the definition for an xterm. This should
contain the same information as the termcap entry on page 308:
xterm|xterm-24|xterms|vs100|xterm terminal emulator (X Window System),
is2=\E7\E[r\E[m\E[?7h\E[?1;3;4;6l\E[4l\E8\E>,
rs2=\E7\E[r\E[m\E[?7h\E[?1;3;4;6l\E[4l\E8\E>,
am, bel=G,
cols#80, lines#24,
clear=\E[H\E[2J, cup=\E[%i%p1%d;%p2%dH,
csr=\E[%i%p1%d;%p2%dr,
cud=\E[%p1%dB, cud1=\n, cuu=\E[%p1%dA, cuu1=\E[A,
cub=\E[%p1%dD, cub1=\b, cuf=\E[%p1%dC, cuf1=\E[C,
el=\E[K, ed=\E[J,
home=\E[H, ht=I, ind=J, cr=M,
km,
smir=\E[4h, rmir=\E[4l, mir,
smso=\E[7m, rmso=\E[m, smul=\E[4m, rmul=\E[m,
bold=\E[1m, rev=\E[7m, blink@, sgr0=\E[m, msgr,
enacs=\E)0, smacs=N, rmacs=O,
smkx=\E[?1h\E=, rmkx=\E[?1l\E>,
kf1=\EOP, kf2=\EOQ, kf3=\EOR, kf4=\EOS,
kf5=\E[15, kf6=\E[17, kf7=\E[18, kf8=\E[19, kf9=\E[20,
kf10=\E[21,
kf11=\E[23, kf12=\E[24, kf13=\E[25, kf14=\E[26, kf15=\E[28,
kf16=\E[29, kf17=\E[31, kf18=\E[32, kf19=\E[33, kf20=\E[34,
kfnd=\E[1, kich1=\E[2, kdch1=\E[3,
kslt=\E[4, kpp=\E[5, knp=\E[6,
kbs=\b, kcuu1=\EOA, kcud1=\EOB, kcuf1=\EOC, kcub1=\EOD,
meml=\El, memu=\Em,
smcup=\E7\E[?47h, rmcup=\E[2J\E[?47l\E8,
sc=\E7, rc=\E8,
il=\E[%p1%dL, dl=\E[%p1%dM, il1=\E[L, dl1=\E[M,
ri=\EM,
dch=\E[%p1%dP, dch1=\E[P,
tbc=\E[3g,
xenl,
xterm-65|xterm with tall window 65x80 (X Window System),
lines#65,
use=xterm,
xterm-bold|xterm with bold instead of underline (X Window System),
smul=\E[1m, use=xterm,
#
# vi may work better with this entry, because vi
# doesnt use insert mode much
xterm-ic|xterm-vi|xterm with insert character instead of insert mode,
smir@, rmir@, mir@, ich1=\E[@, ich=\E[%p1%d@, use=xterm,
The entries look very similar, but there are a few minor differences:
The names for the capabilities may be up to 5 characters long. As a result, the names are
different from the termcap names.
Capabilities are separated by commas instead of colons.
Definitions may be spread over several lines: there is no need to terminate each line of a
definition with a \.
The last character in each entry must be a comma (,). If you remove it, you will thor-
oughly confuse tic.
terminfo functions
terminfo has a number of functions that correspond closely to termlib. They are also the low-
level routines for curses:
#include <curses.h>
#include <term.h>
TERMINAL *cur_term;
Terminfo can use an environment variable TERMINFO, which has a similar function to TERM-
CAP: it contains the name of a directory to search for terminfo entries. Since terminfo is com-
piled, there is no provision for stating the capabilities directly in TERMINFO.
setupterm corresponds to the termcap function tgetent: it reads in the terminfo data
and sets up all necessary capabilities. The parameter term is the name of the terminal,
but may be NULL, in which case setupterm calls getenv to get the name of the terminal
from the environment variable TERM. fd is the file descriptor of the output terminal (nor-
mally 1 for stdout), and error is an error status return address.
setterm is a simplified version of setupterm: it is equivalent to setupterm (term,
1, NULL).
setupterm allocates space for the terminal information and stores the address in the
global pointer cur_term. You can use set_curterm to set it to point to a different ter-
minal entry in order to handle multiple terminals.
del_curterm deallocates the space allocated by setupterm for the terminal oterm.
restartterm performs a subset of setupterm: it assumes that the cur_term is valid,
but that terminal type and transmission speed may change.
tparm substitutes the real values of up to 9 parameters (p1 to p9) into the string str.
This can be used, for example, to create a cursor positioning string like tgoto, but it is
much more flexible.
tputs is effectively the same function as termcap puts described on page 312.
printcap
Termcap was developed in the days where terminal did not always mean a display terminal,
and the capabilities include a number of functions relating to hardcopy terminals. Neverthe-
less, they are not sufficient to describe the properties of modern printers, and BSD systems
have developed a parallel system called printcap, which is a kind of termcap for printers. It is
used primarily by the spooling system.
Printcap differs from termcap in a number of ways:
The printcap capabilities are stored in a file called /etc/printcap, even in systems where
termcap is stored in some other directory.
The syntax of the printcap file is identical to the syntax of termcap, but the capabilities
differ in name and meaning from termcap capabilities.
There is no environment variable corresponding to TERMCAP.
There are no user-accessible routines. The functions used by the spool system actually
have the same names as the corresponding termcap functions, but they are private to the
spooling system. In a BSD source tree, they are located in the file usr.sbin/lpr/com-
mon_source/printcap.c.
A better way to look at printcap is to consider it part of the spooling system, but occasionally
youll need to modify /etc/printcap for a new printer.
We wont go into the details of how make works hereyou can find this information in Man-
aging projects with make, by Andrew Oram and Steve Talbott. In this chapter, well look at
the aspects of make that differ between implementations. Well also take a deeper look at
BSD make, because it is significantly different from other flavours, and because there is very
little documentation available for it.
Terminology
In the course of evolution of make, a change in terminology has taken place. Both the old and
the new terminology are in current use, which can be confusing at times. In the following list,
well look at the terms we use in this book, and then relate them to others which you might
encounter:
A rule looks like:
target: dependencies
command
command
A target is the name by which you invoke a rule. make implicitly assumes that you want
to create a file of this name.
The dependencies are the files or targets upon which the target depends: if any of the
dependencies do not exist, or they are newer than the current target file, or the corre-
sponding target needs to be rebuild, then the target will be rebuilt (in other words, its
commands will be executed). Some versions of make use the terms prerequisite or
source to represent what we call dependencies.
The commands are single-line shell scripts that get executed in sequence if the target
needs to be rebuilt.
317
variables are environment variables that make imports, explicitly named variables, or
implicit variables such as $@ and $<. Variables used to be called macros. They arent
really macros, since they dont take parameters, so the term variable is preferable. BSD
make uses the term local variable for implicit variables. As we will see, they dont cor-
respond exactly. SunOS uses the term dynamic macros for implicit variables.
Internal variables
All versions of make supply internal variables, but the list differs between individual imple-
mentations. Well defer their discussion until we discuss BSD make, on page 324.
SHELL is the name of a shell to be used to execute commands. Note that many versions
of make execute simple commands directly, so you may find that this doesnt have any
effect unless you include a shell metacharacter like ;.
The exact semantics of these variables frequently varies from one platform to anotherin
case of doubt, read your system documentation.
Special targets
All versions of make define a number of targets that have special meanings. Some versions
define additional targets:
.BEGIN is a target to be executed before any other target. It is supported by BSD make.
.INIT is a target to be executed before any other target. It is supported by SunOS and
Solaris make.
.END is a target to be executed after all other targets have been executed. It is supported
by BSD make.
.DONE is a target to be executed after all other targets have been executed. It is supported
by SunOS and Solaris make.
.FAILED is a target to be executed after all other targets have been executed. It is sup-
ported by SunOS and Solaris make.
.INTERRUPT is a target to be executed if make is interrupted. It is supported by BSD
make.
.MAIN is the default target to be executed if no target was specified on the command line.
If this target is missing, make will execute the first target in the Makefile. It is supported
by BSD make.
.MAKEFLAGS is an alternate method to supply flags to subordinate makes. It is supported
by BSD make.
.PATH is an alternate method to specify a search path for files not found in the current
directory. It is supported by BSD make.
.MUTEX is used in System V.4 to synchronize parallel makes.
GNU make uses the target .PHONY to indicate targets that do not create files, such as
clean and install. If by chance you have a file install in your directory, make will
determine that make install does not need to be executed, since install is up to date.
If you use GNU make, you can avoid this problem with:
.PHONY: all install clean
If you dont have GNU make, you can usually solve the problem with
.FORCE:
In this example, .FORCE looks like a special target, as it is meant to. In fact, the name is
not important: you just need a name that doesnt correspond to a real file.
In addition to special targets, BSD make also has special sources (in other words, special
dependencies). Well look at them on page 327.
include directive
Many modern makes allow you to include other files when processing the Makefile. Unfortu-
nately, the syntax is very variable:
In GNU make, the syntax is simply include filename.
In BSD make, the syntax is .include <filename> or .include filename". The
syntax resembles that of the C preprocessor: the first form searches only the system
directories, the second form searches the current directory before searching the system
directories.
In SunOS, Solaris and System V.4 make, the syntax is include filename, but the text
include must be at the beginning of the line.
SunOS and Solaris make automatically include a file make.rules in the current directory
if it exists. Otherwise they include the file /usr/share/lib/make/make.rules.
Conditional execution
A number of versions of make support conditional execution of commands. GNU make has
commands reminiscent of the C preprocessor:
ifeq (${CC},gcc}
${CC} -traditional -O3 -g $*.c -c -o $<
else
${CC} -O $*.c -c -o $<
endif
BSD make has a different syntax, which also vaguely resembles the C preprocessor. Apart
from standard .if, .else and .endif, BSD make also provides an .ifdef directive and
additional operators analogous to #if defined:
.if make (variable) checks whether variable is a main target of make (in other
words, if it was mentioned on the command line that invoked make).
.if empty (variable) tests whether variable represents the empty string.
.if exists (variable) tests whether the file variable exists.
This tells make that the variable (macro) CC should be set to mycc only when executing the
targets foo, bar, and baz.
all:
@echo CFLAGS: ${CFLAGS}
make would loop trying to expand $(CFLAGS). GNU make solves this with simply expanded
variables, which go through one round of expansion only. You specify them with the assign-
ment operator := instead of the usual =. For example:
CFLAGS = -g -O3
CFLAGS := $(CFLAGS) -I/usr/monkey
define directive
You frequently see multi-line shell script fragments in make rules. Theyre ugly and error-
prone, because in conventional make, you need to put this command sequence on a single line
with lots of backslashes and semicolons. GNU make offers an alternative with the define
directive. For example, to check for the existence of a directory and create it if it doesnt
exist, you might normally write
${INSTDIR}:
if [ ! -d $@ ]; then \
mkdir -p $@; \
fi
${INSTDIR}:
${makedir}
This looks almost identical to the first form, but the precedence is lower.
The command line option has the highest priority. This is usually a good idea, but there are
times when you want the declaration in the Makefile to take precedence: you want to override
the definition on the command line. GNU make allows you to specify it with the override
directive. For example, you might want to insist that the optimization level be limited to -O2
if youre generating debugging symbols. In GNU make, you can write:
override CFLAGS=-O2
Functions
As well as variables, GNU make supplies builtin functions. You call them with the syntax
${function arg,arg,arg}. These functions are intended for text manipulation and have
names like subst, findstring, sort, and such. Unfortunately there is no provision for
defining your own functions.
Multiple targets
All forms of make support the concept of multiple targets. They come in two flavours:
Single-colon targets, where the target name is followed by a single colon. Each target of
the same name may specify dependenciesthis is how Makefile dependencies are speci-
fied but only one rule may have commands. If any of the dependencies require the tar-
get to be rebuilt, then these commands will be executed. If you supply commands to
more than one rule, the behaviour varies: some versions of make will print a warning or
an error message, and generally they execute only the last rule with commands. Under
these circumstances, however, BSD make executes the first rule with commands.
Double-colon targets have two colons after the target name. Each of these is indepen-
dent of the others: each may contain commands, and each gets executed only if the
dependencies for that rule require it. Unfortunately, if multiple rules need to be
executed, the sequence of execution of the rules is not defined. Most versions of make
execute them in the sequence in which they appear in the Makefile, but it has been
reported that some versions of BSD make execute in reverse order, which breaks some
Imakefiles.
BSD make
With the Net/2 release, the Computer Sciences Research Group in Berkeley released a com-
pletely new make with many novel features. Most BSD flavoured software that has come out
in the last few years uses it. Unfortunately, it contains a number of incompatibilities with
other makes. It is part of the 4.4BSD Lite distribution see Appendix E, Where to get
sources for further detailsand includes hardcopy documentation, which refers to it as
PMake. This name does not occur anywhere else, though you may see the name bsdmake.
Weve already seen some of the smaller differences between BSD make and other flavours. In
the following sections well look at some more significant differences. On page 327 well
investigate the features of BSD make designed to make configuration easier.
Assignment operators
BSD make supplies five different types of variable assignment:
= functions as in other versions of make: the assignment CFLAGS = -g unconditionally
sets CFLAGS to -g.
+= adds to a definition. If CFLAGS was set as in the previous example, writing CFLAGS
+= -O3 results in a new value -g -O3.
?= assigns a value only if the variable is currently undefined. This can be used to set
default values.
:= assigns and expands immediately. This is the same as the GNU make := assignment.
!= expands the value and passes it to a shell for execution. The result from the shell is
assigned to the variable after changing newline characters to spaces.
Variables
BSD make has clarified the definitions of variables somewhat. Although there is nothing
really new in this area, the terminology is arranged in a more understandable manner. It
defines four different kinds of variables, the first three of which correspond to the kinds of
variable assignment in other makes. In order of priority, they are:
Environment variables
global variables (just called variables in other flavours of make)
command line variables
local variables, which correspond roughly to implicit variables in other makes.
BSD make allows the use of the implicit variable symbols ($@ and friends), but doesnt rec-
ommend it. They dont match very well, anyway, so it makes sense not to use them. Local
variables are really variables that make predefines. Table 19-1 compares them to traditional
make variables:
Trad-
itional BSD Meaning
.ALLSRC, $> The list of all dependencies ("sources") for this target.
$ (GNU make) The list of all dependencies of the current target.
Only the member name is returned for dependencies that rep-
resent an archive member. Otherwise this is the same as BSD
.ALLSRC.
$@ .ARCHIVE The name of the current target. If the target is an archive file
member, the name of the archive file.
$$@ .TARGET, $@ The complete name of the current target, even if it represents
an archive file.1
Variable substitution
In BSD make, variable substitution has reached a new level of complexity. All versions of
make support the syntax ${SRC:.c=.o}, which replaces a list of names of the form foo.c
bar.c baz.c with foo.o bar.o baz.o.. BSD make generalizes this syntax is into
${variable[:modifier[: . . . ]]}. In the following discussion, BSD make uses the term
word where we would normally use the term parameter. In particular, a file name is a word.
modifier is an upper case letter:
all:
@echo Languages: ${LANGS}
@echo Objects: ${OBJS}
@echo Directories: ${DIRS}
@echo C sources: ${CSRCS}
@echo Pascal sources: ${PASSRCS}
@echo Fortran sources: ${FSRCS}
@echo Programs: ${PROGS}
@echo Profiled objects: ${PROFS}
Special sources
In addition to special targets, BSD make includes special sources (recall that source is the
word that it uses for dependencies). Here are the more important special sources:
.IGNORE, .SILENT and .PRECIOUS have the same meaning as the corresponding special
targets in other versions of make.
.MAKE causes the associated dependencies to be executed even if the flags -n (just list
commands, dont perform them) or -t (just update timestamps, dont perform make) are
specified. This enables make to perform subsidiary makes even if these flags are speci-
fied. If this seems a strange thing to want to do, consider that the result of the main make
could depend on subsidiary makes to such an extent that it would not even make sense to
run make -n if the subsidiary makes did not run correctlyfor example, if the subsidiary
make were a make depend.
.OPTIONAL tells make that the specified dependencies are not crucial to the success of
the build, and that make should assume success if it cant figure out how to build the tar-
get.
Specifying dependencies
We have seen that the bulk of a well-written Makefile can consist of dependencies. BSD make
offers the alternative of storing these files in a separate file called .depend. This avoids the
problem of different flavours of makedepend missing the start of the dependencies and adding
them again.
The complete Makefile in the subdirectory cc1 (the main pass of the compiler) reads
# @(#)Makefile 6.2 (Berkeley) 2/2/91
PROG= gcc1
BINDIR= /usr/libexec
SRCS= c-parse.c c-lang.c c-lex.c c-pragma.c \
c-decl.c c-typeck.c c-convert.c c-aux-info.c \
c-iterate.c
.if exists(${.CURDIR}/../lib/obj)
LDADD+= -lgnumalloc
DPADD+= ${LIBGNUMALLOC}
.include <bsd.prog.mk>
The standard release Makefile for gcc is about 2500 lines long. Clearly a lot of work has gone
into getting the BSD Makefiles so small. The clue is the last line of each Makefile:
.include <bsd.subdir.mk>
or
.include <bsd.prog.mk>
These files are supplied with the system and define the hardware and software used on the
system. They are normally located in /usr/share/mk, and you can modify them to suit your
local preferences.
This configuration mechanism has little connection with the new BSD make. It could equally
well have been done, for example, with GNU make or System V make. Unfortunately, the
significant incompatibilities between BSD make and the others mean that you cant just take
the configuration files and use them with other flavours of make.
The BSD system places some constraints on the Makefile structure. To get the best out of it,
you may need to completely restructure your source tree. To quote bsd.README:
Its fairly difficult to make the BSD .mk files work when youre building multiple programs in a
single directory. Its a lot easier [to] split up the programs than to deal with the problem.
Most of the agony comes from making the obj directory stuff work right, not because we
switch to a new version of make. So, dont get mad at us, figure out a better way to handle
multiple architectures so we can quit using the symbolic link stuff.
On the other hand, its remarkably easy to use BSD make configuration once you get used to
it. Its a pity that the make itself is so incompatible with other makes: although the system is
good and works well, its usually not worth restructuring your trees and rewriting your Make-
files to take advantage of it.
There are a couple of other points to note about the configuration method:
make depend is supported via an auxiliary file .depend, which make reads after reading
the Makefile.
The configuration files are included at the end of the Makefile. This is due to the way
that BSD make works: unlike other makes, if multiple targets with a single colon exist,
only the first will be executed, but if multiple declarations of the same variable exist,
only the last one will take effect.
The configuration files consist of one file, sys.mk, which make automatically reads before
doing anything else, and a number of others, one of which is usually included as the last line
in a Makefile. These are usually:
bsd.prog.mk for a Makefile to make an executable binary.
bsd.lib.mk for a Makefile to make a library.
bsd.subdir.mk to make binaries or libraries in subdirectories of the current directory.
In addition, another file bsd.doc.mk is supplied to make hardcopy documentation. In
keeping with the Cinderella nature of such parts of a package, no other file refers to it. If
you want to use it, you include it in addition to one of the other three. This is required
only for hardcopy documentation, not for man pages, which are installed by the other
targets.
sys.mk
sys.mk contains global definitions for all makes. make reads it in before looking for any
Makefiles. The documentation states that it is not intended to be modified, but since it con-
tains default names for all tools, as well as default rules for makes, there is every reason to
believe that you will want to change this file: theres no provision to override these definitions
anywhere else. How you handle this dilemma is your choice. I personally prefer to change
sys.mk (and put up with having to update it when a new release comes), but you could create
another file bsd.own.mk, like FreeBSD does, and put your personal choices in there. The last
line of the FreeBSD sys.mk is
.include <bsd.own.mk>
With this method you can override the definitions in sys.mk with the definitions in
bsd.own.mk. Its up to you to decide whether this is a better solution.
bsd.prog.mk
bsd.prog.mk contains definitions for building programs. Table 19-2 lists the targets that it
defines:
Target Purpose
all Build the single program ${PROG}, which is defined in the Makefile.
clean remove ${PROG}, any object files and the files a.out, Errs, errs, mklog, and core.
cleandir remove all of the files removed by the target clean and also the files .depend,
tags, obj, and any manual pages.
depend make the dependencies for the source files, and store them in the file .depend.
In addition, it supplies default definitions for the variables listed in Table 19-3. The operator
?= is used to ensure that they are not redefined if they are already defined in the Makefile (see
page 324 for more details of the ?= operator).
Variable Purpose
BINGRP Group ownership for binaries. Defaults to bin.
BINOWN Owner for binaries. Defaults to bin.
BINMODE Permissions for binaries. Defaults to 555 (read and execute permission for ev-
erybody).
CLEANFILES Additional files that the clean and cleandir targets should remove.
bsd.prog.mk does not define this variable, but it adds the file strings to the list if
the variable SHAREDSTRINGS is defined.
DPADD Additional library dependencies for the target ${PROG}. For example, if you
write DPADD=${LIBCOMPAT} ${LIBUTIL} in your Makefile, the target depends
on the compatibility and utility libraries.
DPSRCS Dependent sourcesa list of source files that must exist before compiling the
program source files. Usually for a building a configuration file that is required
by all sources. Not all systems define this variable.
LIBC The C library. Defaults to /lib/libc.a.
LIBCOMPAT The 4.3BSD compatibility library. Defaults to /usr/lib/libcompat.a.
LIBCURSES The curses library. Defaults to /usr/lib/libcurses.a.
LIBCRYPT The crypt library. Defaults to /usr/lib/libcrypt.a.
LIBDBM The dbm library. Defaults to /usr/lib/libdbm.a.
LIBDES The des library. Defaults to /usr/lib/libdes.a.
LIBL The lex library. Defaults to /usr/lib/libl.a.
The variables in Table 19-4 are not defined in bsd.prog.mk, but will be used if they have been
defined elsewhere:
Variable Purpose
COPTS Additional flags to supply to the compiler when compiling C object
files.
HIDEGAME If defined, the binary is installed in /usr/games/hide, and a symbolic link
is created to /usr/games/dm.
LDADD Additional loader objects. Usually used for libraries.
LDFLAGS Additional loader flags.
LINKS A list of pairs of file names to be linked together. For example
LINKS= ${DESTDIR}/bin/test ${DESTDIR}/bin/[ links /bin/test
to /bin/[.
NOMAN If set, make does not try to install man pages. This variable is defined
only in bsd.prog.mk, and not in bsd.lib.mk or bsd.man.mk.
PROG The name of the program to build. If not supplied, nothing is built.
SRCS List of source files to build the program. If SRC is not defined, its as-
sumed to be ${PROG}.c.
bsd.lib.mk
bsd.lib.mk contains definitions for making library files. It supplies the same targets as
bsd.prog.mk, but defines or uses a much more limited number of variables:
Variable Purpose
LDADD Additional loader objects.
LIB The name of the library to build. The name is in the same form that you find in
the -l option to the C compilerif you want to build libfoo.a, you set LIB to
foo.
LIBDIR Target installation directory for libraries. Defaults to /usr/lib.
LIBGRP Library group owner. Defaults to bin.
LIBOWN Library owner. Defaults to bin.
LIBMODE Library mode. Defaults to 444 (read access for everybody).
LINTLIBDIR Target directory for lint libraries. Defaults to /usr/libdata/lint.
NOPROFILE If set, only standard libraries are built. Otherwise (the default), both standard li-
braries (libfoo.a) and profiling libraries (libfoo_p.a) are built.*
SRCS List of source files to build the library. Unlike in bsd.prog.mk, there is no default
value.
Given the choice of compiling foo.s or foo.c, bsd.lib.mk chooses foo.s. Like bsd.prog.mk, it
includes bsd.man.mk. Unlike bsd.prog.mk, it does this even if NOMAN is defined.
bsd.subdir.mk
bsd.subdir.mk contains definitions for making files in subdirectories. Since only a single pro-
gram target can be made per directory, BSD-style directory trees tend to have more branches
than others, and each program is placed in its own subdirectory. For example, if I have three
programs foo, bar and baz, I might normally write a Makefile with the rule
all: foo bar baz
As we have seen, this is not easy to do with the BSD configuration scheme. Instead, you
might place all the files necessary to build foo in the subdirectory foo, and so on. You could
then write
SUBDIRS = foo bar baz
.include <bsd.subdir.mk>
bsd.subdir.mk is structured in the same way as bsd.prog.mk. Use bsd.prog.mk for making files
in the same directory, and bsd.subdir.mk for making files in subdirectories. If you want to do
both, use bsd.prog.mk and define SUBDIR instead of SUBDIRS.
bsd.man.mk
bsd.man.mk contains definitions for installing man pages. It is included from bsd.prog.mk and
bsd.lib.mk, so the target and variables are available from both of these files as well. It defines
the target maninstall, which installs the man pages and their links, and uses or defines the
A profiling library is a library that contains additional code to aid profilers, programs that analyze the
CPU usage of the program. We dont cover profiling in this book.
Variable Meaning
MANDIR The base path of the installed man pages. Defaults to /usr/share/man/cat. The
section number is appended directly to MANDIR, so that a man page foo.3 would
be installed in /usr/share/man/cat3/foo.3.
MANGRP The group that owns the man pages. Defaults to bin.
MANOWN The owner of the man pages. Defaults to bin.
MANMODE The permissions of the installed man pages. Defaults to 444 (read permission
for anybody).
MANSUBDIR The subdirectory into which to install machine specific man pages. For example,
i386 specific pages might be installed under /usr/share/man/cat4/i386. In this
case, MANSUBDIR would be set to /i386.
MANn (n has the values 1 to 8). Manual page names, which should end in .[1-8]. If
no MANn variable is defined, MAN1=${PROG}.1 is assumed.
MLINKS A list of pairs of names for manual page links. The first filename in a pair must
exist, and it is linked to the second name in the pair.
bsd.own.mk
Not all variants of the BSD configuration system usebsd.own.mk. Where it is supplied, it con-
tains default permissions, and may be used to override definitions in sys.mk, which includes it.
bsd.doc.mk
bsd.doc.mk contains definitions for formatting hardcopy documentation files. It varies signifi-
cantly between versions and omits even obvious things like formatting the document. It does,
however, define the variables in Table 19-7, which can be of use in your own Makefile:
Variable Meaning
PRINTER Not a printer name at all, but an indicator of the kind of output format to be used. This is the
argument to the troff flag -T. Defaults to ps (PostScript output).
BIB The name of the bib processor. Defaults to bib.
COMPAT Compatibility mode flag for groff when formatting documents with Berkeley me macros.
Defaults to -C.
The C language
The C language has evolved a lot since its appearance in the early 70s. It started life as a
Real Mans language, cryptic, small, tolerant of obscenities almost to the point of encouraging
them, but now it has passed through countless committees and has put on weight and become
somewhat sedate, pedantic and much less tolerant. Along with this, of course, it has devel-
oped a whole lot of idiosyncracies that plague the life of the portable software writer. First,
lets take a look at the flavours that are commonly available.
337
When the parameter msg is non-NULL, it is copied into the string message. If you call this
function with a NULL message, it will display the last message again. For example:
complain (NULL); prints Nothing to complain about
complain ("Bad style"); prints Bad style
complain (NULL); prints Bad style
This may fail with modern C compilers: The ANSI Standard says that string constants are not
writable, but real-world compilers differ in the way they handle this situation.
UNIX C
A period of over ten years elapsed between the publication of K&R and the final adoption of
the ANSI C standard. During this time, the language didnt stand still, but there was no effec-
tive standards document beyond K&R. The resultant evolution in the UNIX environment is
based on the Portable C Compiler first described in the paper Portability of C Programs and
the UNIX System published by S. C. Johnson and Dennis Ritchie in 1978, and is frequently
referred to as UNIX C. This is not a standard, or even a series of standardsits better to
consider it a set of marginally compatible extensions to K&R C. You can find more informa-
tion in The evolution of CPast and Future by L. Rosler, but you cant rely on the degree to
which your particular compiler (or the one for which your software package was written)
agrees with that description. From a present-day standpoint, its enough to know that these
extensions exist, and otherwise treat the compilers like K&R. In case of doubt, the documen-
tation that comes with the compiler is about the only even remotely reliable help. Heres a
brief summary of the sort of things that had changed by the time The evolution of CPast
and Future appeared:
Optional function prototyping similar to that of ANSI C was introduced. One difference
exists: if a function accepts a variable number of parameters, UNIX C uses the form
int printf (char *format, );
The enum type specifies a way to define classes of constants. For example, traditionally I
could write:
#define RED 0
#define GREEN 1
#define BLUE 2
int colour;
int x;
colour = BLUE;
x = RED;
This syntax is intended to make error checking easier. As you can see in the second
example, there seems to be something wrong with the assignment to x, which was not
evident in the K&R example. The compiler can see it too, and should complain,
although many modern compilers compile the second program without any comment. In
addition, the symbols are visible to the compiler. This means that the debugger can use
them as well: preprocessor macros never make it to the code generation pass of the com-
piler, so the debugger doesnt know about them. The keyword const was added to spec-
ify that a variable may not be changed in the course of program execution.
The preprocessor directive #elif was added.
The preprocessor pseudofunction defined (identifier) was added.
The data type void was added.
ANSI C
In 1989, the C programming language was finally standardized by the American National
Standards Institute (ANSI) as standard X3.159-1989. In the following year it was adopted by
the International Standards organization (ISO) as standard ISO/IEC 9899:1990. There are
minor textual differences in the two standards, but the language defined is the same in each.
The existence of two standards is good for a certain amount of confusion: some people call it
ANSI C, some call it Standard C, and I suppose you could call it ISO C, though I havent
heard that name. I call it ANSI C because the name is more specific: the word Standard
doesnt make it obvious which standard is implied.
The following discussion is intended to show the differences between ANSI C and older ver-
sions. Its not intended to teach you ANSI Csee Practical C Programming, by Steve
Oualline, and the POSIX Programmers Guide by Donald A. Lewine for that information.
ANSI C introduced a large number of changes, many of them designed to help the compiler
detect program errors. You can find a reasonably complete list in Appendix C of K&R. Here
are the most important from the standpoint of porting software:
A number of changes have been made in the preprocessor. Well look at these on page
342.
The keywords void, signed and const were adopted from the Portable C compiler.
The keyword volatile was added to tell an optimizer not to assume that the value of
the variable will stay the same from one reference to another. Variables normally stay
unchanged unless you execute an explicit assignment statement or call a function, and
most optimizers rely on this behaviour. This assumption may not hold true if a signal
interrupts the normal course of execution, or if you are sharing the memory with another
process. If the value of a variable might change without your influence, you should
declare the variable volatile so that the optimizer can handle it correctly. We saw an
example of this kind of problem in Chapter 13, Signals, page 200.
You can state the type of numeric constants explicitly: for example, you can write a long
constant 0 as 0L, and a double 0 would be 0D.
Implicit string literal concatenation is allowed the following two lines are completely
equivalent:
"first string" "second string"
"first stringsecond string"
and what value (if any) it returns. A function definition is the code for the function, and
includes the declaration.
Strict ANSI C function definitions and declarations include function protyping, which
specifies the nature of each parameter, though most implementations allow old-style defi-
nitions. Consider the following function definition in K&R C:
foobar (a, b, c, d)
char *c;
struct baz *a;
{
body
}
This definition does not specify the return type of the function; it may or may not return
int. The types of two of the parameters are specified, the others default to int. The
parameter type specifiers are not in the order of the declaration. In ANSI C, this would
become:
void foobar (struct baz *a, int b, char *c, int d)
{
body
}
This definition states all types explicitly, so we can see that foobar does not, in fact,
return any value.
The same syntax can also be used to declare the function, though you can also abbreviate
it to:
void foobar (struct baz *, int, char, int);
This helps catch one of the most insidious program bugs: consider the following code,
which is perfectly legal K&R:
extern foobar ();/* define foobar without parameters */
int a, b; /* two integers */
struct baz *c; /* and a struct baz */
In this example, I have supplied the parameters to foobar in the wrong sequence: the
struct baz pointer is the first parameter, not the third. In all likelihood, foobar will
try to modify the struct baz, and will use the value of apossibly a small inte-
ger to do this. If I call foobar without parameters, the compiler wont notice, but by
the time I get my almost inevitable segmentation violation, foobar will probably have
overwritten the stack and removed all evidence of how the problem occurred.
character trigraph
# ??=
[ ??(
\ ??/
] ??)
??
{ ??<
| ??!
} ??>
??-
To show what this means, lets look at a possibly barely recognizable program:
??=include <unistd.h>
main ()
??<
printf ("Hello, world??/n");
??>
Not surprisingly, most programmers hate the things. To quote the gcc manual: You
dont want to know about this brain-damage. Many C compilers, including the GNU C
compiler, give you the opportunity to turn off support for trigraphs, since they can bite
you when youre not expecting them.
Any line may end with \, indicating that it should be splicedin other words, the pre-
processor removes the \ character and the following newline character and joins the line
to the following line. K&R C performed line splicing only during the definition of pre-
processor macros. This can be dangerous: trailing blanks can nullify the meaning of the \
character, and its easy to oversee one when deleting lines that follow it.
Unlike UNIX C, formal macro parameters in strings are not replaced by the actual
parameters. In order to be able to create a string that includes an actual parameter, the
operator # was introduced. A formal parameter preceded by a # is replaced by the actual
parameter surrounded by string quotes. Thus
will be replaced by
open ("/usr/lib/libc.a");
In many traditional versions of C, you could have got the same effect from:
#define foo(x) open ("x")
foo (/usr/lib/libc.a);
In K&R C, problems frequently arose concatenating two parameters. Since both param-
eter names are valid identifiers, you cant just write one after the other, because that
would create a new valid identifer, and nothing would be substituted. For example, con-
sider the X11 macro Concat, which joins two names together to create a complete path
name from a directory and a file name:
Concat(dir, file);
because that will always just give me the text dirfile, which isnt much use. The solu-
tion that the X Consortium used for K&R C was:
#define Concat(dir,file)dir/**/file
This relies on the fact that most C compilers derived from the portable C compiler sim-
ply remove comments and replace them by nothing. This works most of the time, but
there is no basis for it in the standard, and some compilers replace the sequence /**/
with a blank, which breaks the mechanism. ANSI C introduced the operator ## to
address this problem. ## removes itself and any white space (blanks or tab characters) to
either side. For ANSI C, Imake.tmpl defines Concat as
#define Concat(dir,file)dir##file
The #include directive now allows the use of preprocessor directives as an argument.
imake uses this to #include the <vendor>.cf file.
Conditional compilation now includes the #elif directive, which significantly simplifies
nested conditional compilation. In addition, a number of logical operators are available:
|| and && have the same meaning as in C, and the operator defined checks whether its
operand is defined. This allows code like:
#if defined BSD || defined SVR4 || defined ULTRIX
foo
#elif defined SVR3
bar
#endif
If you want, you can surround the operand of defined with parentheses, but you dont
need to.
The use of the preprocessor directive #line, which had existed in previous versions of
C, was formalized. #line supports preprocessors that output C codesee page 88 for
an example. #line tells the compiler to reset the internal line number and file name
used for error reporting purposes to the specified values. For example if the file bar.c
contains just
#line 264 "foo.c"
slipup!
Although the error was really detected on line 2 of bar.c, the compiler reports the error as
if it had occurred on line 264 of foo.c.
The line slipup! suggests that it is there to draw attention to an error. This is a fairly
common technique, though its obviously just a kludge, especially as the error message
requires you to look at the source to figure out what the problem is. ANSI C introduced
another directive to do the Right Thing. Instead of slipup!, I can enter:
#error Have not finished writing this thing yet
I couldnt write Havent, because that causes gcc to look for a matching apostrophe ().
Since there isnt one, it would die with a less obvious message, whether or not an error
really occurred.
To quote the Standard:
A preprocessor line of the form # pragma token-sequenceopt causes the processor to perform
an implementation-dependent action. An unrecognized pragma is ignored.
This is not a Good Thing. Implementation-dependent actions are the enemy of portable soft-
ware, and about the only redeeming fact is that the compiler ignores an unrecognized pragma.
Since almost nobody uses this feature, you can hope that your compiler will, indeed, ignore
any pragmas it finds.
Assertions
Assertions provide an alternative form of preprocessor conditional expression. They are spec-
ified in the form
In the terminology of the documentation, this asserts (states) that the answer to question is
answer. You can test it with the construct:
#if #question(answer)
...
#endif
The code between #if and #endif will be compiled if the answer to question is answer.
An alternative way to use this facility is in combination with the compiler directive -Aques-
tion(answer). This method is intended for internal use by the compiler: typically, it tells
the compiler the software and platform on which it is running. For example, compiling bar.c
on UNIXWare 1.0 with gcc and the -v flag reveals:
/usr/local/lib/gcc-lib/i386-univel-sysv4.2/2.4.5/cpp \
-lang-c -v -undef -D__GNUC__=2 -Di386 -Dunix -D__svr4__ \
-D__i386__ -D__unix__ -D__svr4__ -D__i386 -D__unix \
-D__svr4__ -Asystem(unix) -Acpu(i386) -Amachine(i386) \
bar.c /usr/tmp/cca000Nl.i
The -A flags passed by gcc to the preprocessor specify that this is a unix system and that the
cpu and machine are both i386. It would be nice if this information stated that the operating
system was svr4, but unfortunately this is not the default for System V.4. gcc has also retro-
fitted it to System V.3, where the assertion is -Asystem(svr3), which makes more sense,
and to BSD systems, where the assertion is -Asystem(bsd).
C++
C++ is an object-oriented evolution of C that started in the early 80s, long before the ANSI C
standard evolved. It is almost completely upwardly compatible with ANSI C, to a large extent
because ANSI C borrowed heavily from C++, so we dont run much danger by considering it
the next evolutionary step beyond ANSI C.
The last thing I want to do here is explain the differences between ANSI C and C++: The
Annotated C++ Reference Manual, by Margaret A. Ellis and Bjarne Stroustrup, spends
nearly 450 very carefully written pages defining the language and drawing attention to its
peculiarities. From our point of view, there is not too much to say about C++.
One of the more popular C++ translators is AT&Ts cfront, which, as the name suggests, is a
front-end preprocessor that generates C program code as its output. Although this does not
make the generated code any worse, it does make debugging much more difficult.
Since C++ is almost completely upwards compatible from ANSI C, a C++ compiler can usu-
ally compile ANSI C. This assumes well-formed ANSI C programs: most ANSI C compilers
accept a number of anachronisms either with or without warnings for example, K&R-style
function definitions. The same anachronisms are no longer part of the C++ language, and
cause the compilation to fail.
C++ is so much bigger than C that it is not practicable to even think about converting a C++
program to C. Unless there are some really pressing reasons, its a whole lot easier to get hold
of the current version of the GNU C compiler, which can compile both C and C++ (and
Objective C, if youre interested).
C and C++ have different function linking conventions. Since every C++ program calls C
library functions, there is potential for errors if you use the wrong calling convention. We
looked at this aspect in Chapter 17, Header files, on page 286.
Other C dialects
Before the advent of ANSI C, the language was ported to a number of non-UNIX architec-
tures. Some of these added incompatible extensions. Many added incompatible library calls.
One area is of particular interest: the Microsoft C compiler, which was originally written for
MS-DOS. It was subsequently adapted to run under XENIX and SCO UNIX System V.
Since our emphasis is on UNIX and UNIX-like systems, well talk about the XENIX com-
piler, though the considerations also apply to SCO UNIX and MS-DOS.
The most obvious difference between the XENIX C compiler and most UNIX compilers is in
the flags, which are described in Appendix B, Compiler flags, but a couple of architectural
limitations have caused incompatibilities in the language. Well look at them in the following
section.
Along with three pointer types, MS-DOS C uses a number of different executable formats.
Each of them has default pointer sizes associated with them. You choose your model by sup-
plying the appropriate flag to the compiler, and you can override the default pointer sizes with
the explicit use of the keywords near, far or (where available) huge:
The tiny model occupies a single segment and thus can always use near addresses. Apart
from the obvious compactness of the code, this model has the advantage that it can be
converted to a .COM file.
The small model occupies a single data segment and a single code segment. Here, too,
you can always use near pointers, but you need to be sure youre pointing into the correct
segment.
The medium model (sometimes called middle model) has multiple code segments and a
single data segment. As a result, code pointers are far and data pointers are near.
The compact model is the inverse of the medium model. Here, code is restricted to one
segment, and data can have multiple segemnts. Static data is restricted to a single seg-
ment. As a result, code pointers are near and data pointers are far.
The large model can have multiple code and multiple data segments. Static data is
restricted to a single segment. All pointers are far.
The huge model is like the large model except that it can have multiple static data seg-
ments. The name is unfortunate, since it suggests some connection with huge pointers.
In fact, the huge model uses far pointers.
What does this mean to you? If youre porting from MS-DOS to UNIX, you may run into
these keywords near, far and huge. This isnt a big deal: you just need to remove them, or
better still, define them as an empty string. You may also find a lot of pointer checking code,
which will probably get quite confused in a UNIX environment. If you do find this kind of
code, the best thing to do is to ifdef it out (#ifndef unix).
If youre converting from UNIX to MS-DOS, things can be a lot more complicated. Youll be
better off using a 32-bit compiler, which doesnt need this kind of kludge. Otherwise you
may have to spend a considerable amount of time figuring out the memory architecture most
suitable for your package.
The assembler code output by MS-DOS compilers is in the standard Intel mnemonics,
which are not compatible with UNIX assemblers.
Many MS-DOS compilers combine the preprocessor and the main compiler pass, which
makes for faster compilation and less disk I/O.
Many rely on the Microsoft linker, which was not originally written for C, and which has
significant limitations.
Many MS-DOS compilers still run in real mode, which limits them to 640K code and
data. This is a severe limitation, and it is not uncommon to have to modify programs in
order to prevent the compiler from dying of amnesia. This leads to a different approach
with header files, in particular: in UNIX, its common to declare everything just in case,
whereas in MS-DOS it may be a better idea to not declare anything unless absolutely
necessary.
Compiler organization
The traditional UNIX compiler is derived from the Portable C Compiler and divides the com-
pilation into four steps, traditionally called phases or passes, controlled by the compiler con-
trol program cc. Most more modern compilers also adhere to this structure:
1. The preprocessor, called cpp, reads in the the source files and handles the preprocessor
directives (those starting with #) and performs macro substitution.
2. The compiler itself, usually called cc1, reads in the preprocessor output and compiles to
assembler source code. In SunOS, this pass is called ccom.
3. The assembler as reads in this output and assembles it, producing an object file.
4. The loader takes the object file or files and links them together to form an executable. To
do so, it also loads a low-level initialization file, normally called crt0.o, and searches a
number or libraries.
cc usually performs these passes invisibly. The intermediate outputs are stored in temporary
files or pipes from one pass to the next. It is possible, however, to call the passes directly or to
tell cc which pass to execute well look at how to do that in the next section. By conven-
tion, a number of suffixes are used to describe the intermediate files. For example, the GNU
Heres what you need to do to go through the compilation of foo.c to the executable foo, one
pass at a time:
$ gcc -E foo.c -o foo.i preprocess
$ gcc -S foo.i compile
$ gcc -c foo.s assemble
$ gcc foo.o -o foo link
There are slight variations in the form of the commands: if you dont tell the preprocessor
where to put the output file, gcc writes it to stdout. Other preprocessors may put a special suf-
fix on the base file name, or if you specify the -o flag, the compiler might put it in the file you
specify. If you dont tell the linker where to put the output file, it writes to a.out.
Compiling an object file from an assembler file is the same as compiling from a source file or
a preprocessed filegcc decides what to do based on the suffix of the input file.
You can also run any combination of contiguous passes like this:
$ gcc -S foo.c preprocess and compile
$ gcc -c foo.c preprocess, compile and assemble
$ gcc -o foo foo.c preprocess, compile, assemble, link
$ gcc -c foo.i compile and assemble
$ gcc -o foo foo.i compile, assemble, link
$ gcc -o foo foo.s assemble and link
The location of the C compiler is, unfortunately, anything but standardized. The control pro-
gram cc is normally in /usr/bin, or occasionally in /bin, but the other components might be
stored in any of the following: /usr/lib, /usr/ccs/lib (System V.4), /usr/lib/cmplrs/cc (MIPS) or
/usr/local/lib/gcc-lib (gcc on most systems).
The C preprocessor
You can use the preprocessor cpp for other purposes than preprocessing C source code: it is a
reasonably good macro processor, and it has the advantage that its functionality is available on
every system with a C compiler, though in some cases it is available only via the C compiler.
It is one of the mainstays of imake, and occasionally packages use it for other purposes as
well.
There are two ways to invoke cpp: you can invoke it with cc and the -E flag, or you can start it
directly. If at all possible, you should start it via cc rather than running it directly. On some
systems you cant rely on cc to pass the correct flags to cpp. You also cant rely on all ver-
sions of cpp to use the same flagsyou cant even rely on them to be documented. You can
find a list comparing the more common preprocessor flags in Appendix B, Compiler flags,
page .
use features that were not universally implemented, whereas the ANSI versions tend to pay
more attention to the standard. If you do run into a bug, chances are someone has seen it
before and has taken steps to work around it. In addition, compiling for ANSI usually means
that the prototypes are declared in ANSI fashion, which increases the chance of subtle type
conflicts being caught.
Some things that neither you nor the Makefile may expect are:
gcc compiles both K&R (-traditional) and ANSI dialects. However, even some soft-
ware supplied by the Free Software Foundation breaks when compiled with gcc unless
the -traditional flag is used.
Many compilers do not compile correctly when both optimization and debugging infor-
mation are specified (-O and -g flags), though most of them recognize the fact and turn
off one of the flags. Even if the compiler ostensibly supports both flags together, bugs
may prevent it from working well. For example, gcc version 2.3.3 generated invalid
assembler output for System V.4 C++ programs if both flags were specified. Even when
compilers do create debugging information from an optimizing compilation, the results
can be confusing due to the action of the optimizer:
The optimizer may remove variables. As a result, you may not be able to set or dis-
play their values.
The optimizer may rearrange program flow. This means that single-stepping might
not do what you expect, and you may not be able to set breakpoints on certain lines
because the code there has been eliminated.
Some optimizers remove stack frames,* which makes for faster code, particularly
for small functions. gcc will do this with the -O3 option.
Stack frame removal in particular makes debugging almost impossible. These arent
bugs, theyre features. If they cause problems for you, you will need to recompile with-
out optimization.
Some compilers limit the length of identifiers. This can cause the compiler to treat two
different identifiers as the same thing. The best thing to do if you run into this problem is
to change the compiler: modern compilers dont have such limits, and a compiler that
does is liable to have more tricks in store for you.
With a System V compiler, you might find:
$ cc -c frotzel.c -o frotzel.o
cc: Error: -o would overwrite frotzel.o
System V compilers use the flag -o only to specify the name of the final executable,
which must not coincide with the name of the object file. In many Makefiles from the
BSD world, on the other hand, this is the standard default rule for compiling from .c to
.o.
* See Chapter 21, Object files and friends, page 377, for further information on stack frames.
All C compilers expect at least some of their flags in a particular sequence. The docu-
mentation is frequently hazy about which operands are sequence-sensitive, or what inter-
actions there are between specific operands.
The last problem bears some more discussion. A well-documented example is that the linker
searchs library specifications (the -l option) in the sequence in which they are specified on
the compiler invocation linewell investigate that in more detail in Chapter 21, Object files
and friends, page 373. Heres an example of another operand sequence problem:
$ cc foo.c -I../myheaders
If foo.c refers to a file bar.h in the directory ../myheaders, some compilers wont find the
header because they dont interpret the -I directive until after they try to compile foo.c. The
man page for System V.4 cc does state that the compiler searches directories specified with -I
in the order in which they occur, but it does not relate this to the sequence of operands and file
names.
353
program that creates object files from scratch. The linker or link editor joins object files
together to form a larger object file, and debuggers access specific debugging information in
the object file. These are the only programs that have intimate understanding of the object file
format.
A number of smaller programs do relatively trivial things with object files:
The archiver ar is normally used for archiving binary files, but it does not know very
much about their contents.
The name list display program nm displays the symbol table or name list of an object file
or an archive of object files. Well look at the symbol table in more detail on page 363.
size displays size information from an object file.
strings displays printable strings in an object file.
strip removes unnecessary information from an object file.
In the rest of this chapter, well look at the following topics:
The kernel process model that the object file supports.
The assembler, including some of the syntax, the symbol table, relocation, and debug-
ging symbols.
The linker, including the way it searches libraries, and some of the problems that can
occur during linking.
The internal structure of function libraries, and how this affects what they can and cannot
do.
How emacs and TEX dump themselves as object files.
How exec starts programs.
Object formats
The purpose of object files is to make it as easy as possible to start a process, so it makes
sense to look at the process image in memory first. Modern UNIX systems run on stack-
based systems with virtual memory. We touched on the concept of virtual memory in Chapter
11, Hardware dependencies, on page 155. Since UNIX is a multiprogramming system, it is
possible for more than one process to run from a single object file. These facts have a signifi-
cant influence on the way the system manages processes. For each process, the system allo-
cates at least three segments in which program code and data is stored:
A text segment, which contains the executable code of the program and read-only data.
Modern systems create code where the program may not write to its text segment it is
so-called pure text. This has two significant advantages for the memory manager: first,
all processes in the system that are run from this program can share the same text seg-
ment, which significantly reduces the memory requirements when, say, 20 copies of a
shell are running. In addition, since the text is not modified, the memory management
routines never need to swap it out to disk. The copy on disk is always the same as the
copy in memory. This also means that the copy on disk can be the copy in the object file:
it does not take up any space in the swap partition.
Older systems also provided for impure text segments that could be modified by the program.
This usage is obsolete, but it is still supported by modern systems.
A data segment. This consists of two parts:
Global data that has been initialized in the program. This data can be modified, of
course, so it takes up space in the swap partition, but the first time the page is refer-
enced, the memory manager must load it from the object file.
bss* data, non-initialized global data. Since the data is not initialized, it does not
need to be loaded from a file. The first time the page is referenced, the memory
manager just creates an empty data page. After that, it gets paged to the swap parti-
tion in the same way as initialized data.
A stack segment. Like bss data, the stack segment is not initialized, and so is not stored
in the object file. Unlike any of the other segments, it does not contain any fixed
addresses: at the beginning of program execution, it is almost empty, and all data stored
in it is relative to the top of the stack or another stack marker. Well look at stack organi-
zation in more detail on page 377.
In addition, many systems have library segments. From the point of view of memory
management, these segments are just additional text and data segments, but they are
loaded at run time from another object file, the library file.
Older systems without virtual memory stored the data segment below the stack segment with
a gap in between, the so-called break. The stack grew down into the break as the result of
push or call instructions, and the data segment grew up into the break as the result of system
calls brk and sbrk (set break). This additional space in the data segment is typically used for
memory allocated by the library call malloc. With a virtual memory system, the call to sbrk
is no longer necessary, but some versions of UNIX still require it, and all support it. Table
21-1 summarizes this information:
* The name comes from the assembler directive bss (Block Starting with Symbol), which was used in
older assemblers to allocate uninitialized memory and allocate the address of the first word to the label
of the directive. There was also a directive bes (Block Ending with Symbol) which allocated the address
of the last word to the label.
Object files contain the information needed to set up these segments. Before we continue, we
should be aware of a terminology change:
The object file for a process is called a program.
The images of process segments in an object file are called sections.
There are three main object file formats in current use:
The a.out format is the oldest, and has remained essentially unchanged since the Seventh
Edition. It supplies support for a text section and a data section, as well as relocation
information for both sections. It is used by XENIX and BSD systems.
The COFF (Common Object File Format) was introduced in System V, and offers an
essentially unlimited number of segments, including library segments. It is now obsoles-
cent, except for use in Microsoft Windows NT.
The ELF (Executable and Linking Format) format was introduced for System V.4. From
our point of view, it offers essentially the same features as COFF. ELF shows promise as
the executable format of the future, since it greatly simplifies the use of shared libraries.
Currently the Linux project is moving from a.out to ELF.
With the exception of library segments, theres not much to choose between the individual
object formats, but the internal structures and the routines that handle them are very different.
Lets take an a.out header from a BSD system as an example. The header file sys/exec.h
defines:
struct exec
{
long a_magic; /* magic number */
unsigned long a_text; /* text segment size */
unsigned long a_data; /* initialized data size */
unsigned long a_bss; /* uninitialized data size */
unsigned long a_syms; /* symbol table size */
unsigned long a_entry; /* entry point */
unsigned long a_trsize; /* text relocation size */
unsigned long a_drsize; /* data relocation size */
};
/* a_magic */
#define OMAGIC 0407 /* old impure format */
* Why 0x1000? Its a wonderful debugging aid for catching NULL pointers. If the first page of memory
is not mapped, youll get a segmentation violation or a bus error if you try to access data at that address
The assembler
Assembly is the second oldest form of programming*. It is characterized by being specific
about the exact instructions that the machine executes, which makes an assembler program
much more voluminous than a higher level language. Nevertheless, there is nothing difficult
about it, its just tedious.
Assembler programming involves two aspects that dont have much in common:
The instruction set of the machine in question. The best source of information for this
aspect is the hardware description of the machine. Even if you get an assembler manual
for the machine, it will not be as authoratative as the hardware description.
The syntax of the assembler. This is where the problems start: first, little documentation
is available, and secondly, assembler syntax diverges greatly, and the documentation you
get may not match your assembler.
The i386 is a particularly sorry example of incompatible assembler syntax. The UNIX assem-
blers available for the i386 (at least three of them, not even compatible with each other) use
modified forms of the old UNIX as syntax, whereas all books about the assembler and the
hardware of the i386 use a syntax related to the Microsoft assembler MASM. They dont even
agree on such basic things as the names of the instructions and the sequence of the operands.
Although nowadays it is used almost only for assembling compiler output, as frequently
offers features specifically intended for human programmers. In particular, most assemblers
support some kind of preprocessing: they may use the macro preprocessor m4 or the C pre-
processor when processing assembler source. See the description of the flags in Appendix C,
Assembler directives and flags, page 415, for more information.
Assembler syntax
Assembler syntax is a relatively involved topic, but there are some general rules that apply to
just about every assembler. In this section, well see how to fight our way through an assem-
bler listing.
Assemblers are line-oriented: each instruction to the assembler is placed on a separate
line.
An instruction line consists of four parts:
If the optional label is present, the assembler assigns a value to it. For most instruc-
tions, the value is the current value of the location counter, the relative address of
the instruction in the current section. In UNIX, if the label is present it is followed
by a colon (:). Other assemblers frequently require that only labels start at the
beginning of the line, and recognize them by this fact.
* The oldest form of programming, of course, used no computational aids whatsoever: in some form or
another, the programmer wrote down direct machine code and then entered into memory with a loader or
via front-panel switches. Assembly added the symbolic character to this operation.
The assembler usually translates the source file in a single pass. This means that
when it encounters the name of a label that is further down in the source file, it can-
not know its value or even if it exists. Some assemblers require that the name of the
label be followed with the letter b (backwards) for labels that should have already
been seen in the text, and f (forwards) for labels that are further down. In order to
avoid ambiguity, these assemblers also require that the labels be all digits. Many
other assemblers also support this syntax, so 1b is not a good name for a label.
The next field is the instruction. In this context, assembler instructions are com-
mands to the assembler, and may be either directives, which tell the assembler to do
something not directly related to emitting code, or machine instructions, which emit
code. In UNIX, directives frequently start with a period (.).
The third field contains the operands for the instruction. Depending on the
instruction, they may not be required.
The fourth field is a comment field. It is usually delimited by a hash mark (#).
The operands of instructions that take a source operand and a destination operand are
usually specified in the sequence src, dest.
Register names are usually preceded with a % sign.
Literal values are usually preceded with a $ sign.
For example, consider the instruction:
fred: movl $4,%eax # example
This instruction emits a movl instruction, which moves the literal* value 4 to the register eax.
The symbol fred is set to the address of the instruction.
We cant go into all the details of the assembly language for all machines, but the descriptions
in Appendix C, Assembler directives and flags, page 415, will hopefully give you enough
insight to be able to read existing assembler source, though youll need more information
before you can write it. One of the few reasonably detailed as manuals is Using as, by Dean
Elsner and Jay Fenlason, which is included as part of the GNU binutils distribution.
Assembler symbols
Local symbols define instruction addresses. High-level constructs in C such as if, while and
switch require a number of jump (go to) instructions in assembler, and the compiler must
generate labels for the instructions.
Local symbols are also used to label literal data constants such as strings.
Global symbols defined in the source. The word global has different meanings in C and
assembler: in C, it is any symbol defined in the data or text segments, whether or not it is
* movl means move long, not move literal. In this particular assembler, we know that it is a literal
value because of the $ symbol, just as we know that eax is a register name because it is preceded by a %
sign.
visible outside the module. In assembler, a global symbol is one that is visible outside the
module.
There are a couple of points to note here:
C local variables are generated automatically on the stack and do not retain their names
after compilation. They do not have a fixed location, since their position on the stack
depends on what was already on the stack when the function was called. If the function
is recursive, they could even be in many different places on the stack at the same time.
As a result, there is nothing that the assembler or the linker can do with the symbols, and
the compiler discards them.
There is a possibility of conflict between the local symbols generated by the compiler
and global symbols declared in the program. Most compilers avoid this conflict by
prepending an underscore (_) to all symbols defined in the program, and not using the
underscore for local symbols. Others solve the problem by prepending local symbols
with a period (.), which is not legal in a C identifier.
To see how this all works, lets take the following small program and look at different aspects
of what the compiler and assembler do with it in the next few sections:
Example 211:
char global_text [] = "This is global text in the data area";
void inc (int *x, int *y)
{
if (*x)
(*x)++;
else
(*y)++;
puts (global_text); /* this is an external function */
puts ("Thats all, folks");
}
We compile this program on a BSD/OS machine using gcc version 2.5.8, with maximum opti-
mization and debugging symbols:
$ gcc -O2 -g -S winc.c
The -S flag tells the compiler control program to stop after running the compiler. It stores the
assembly output in winc.s, which looks like this:
Example 212:
.file "winc.c"
gcc2_compiled.:
___gnu_compiled_c:
.stabs "/usr/lemis/book/porting/grot/",100,0,0,Ltext0 name of the source directory
.stabs "winc.c",100,0,0,Ltext0 name of the source file
.text select text section
Ltext0: internal label: start of text
.stabs "int:t1=r1;-2147483648;2147483647;",128,0,0,0
.stabs "char:t2=r2;0;127;",128,0,0,0
... a whole lot of standard debugging output omitted
Well look at various aspects of this output in the next few sections. For now, we should
notice:
As advertised, the names of the global symbols global_text, inc and puts have been
changed to _global_text, _inc and _puts.
The compiler has created the local symbols Ltext0, LC0, LBB2, LBE2, L2 and L3.
Clearly it likes to start the names of local symbols with the letter L, and distinguish them
with numbers at the end. But what has happened to L1, for example? The compiler gen-
erated it, but the optimizer optimized it away. If you compile this same program without
the optimizer, the labels will all still be there.
The compiler has assigned the local symbol LC0 to the string "Thats all, folks" so
that the assembler can refer to it.
The variables x and y have disappeared, since they exist only on the stack.
Relocation information
Example 21-2 shows another dilemma that afflicts the linker: the program is not complete. It
refers to the external symbol _puts, and in addition it does not have a main function: the only
way to use it is for a function in another object file to call _inc. In order to do this, we need
to give the linker additional information:
Information about the names of the external symbols that the object file references
(_puts in our example).
Information about symbols defined in the object file that can be referenced by other
object files (_global_text and _inc in our example).
Information about where external symbols are referenced in the object code.
Information about where locations in the text and data segments are referenced in the
object code.
Why do we need to know where internal locations are referenced? The linker takes the text
and data sections from a large number of object files and makes a single text section and a sin-
gle data section out of them. The locations of the original sections from the individual object
files differ from one occasion to the next, but the addresses in the final executable must reflect
the correct references. If an instruction refers to an address in the data or text section or an
external symbol, the assembler cant just put the address of the item in the instruction, since
the address is allocated by the linker. Instead, it places the offset from the beginning of the
text or data section or from the external symbol into the instruction or data word, and gener-
ates a relocation record in the output file. These relocation records contain the following
information:
The address of the data that needs to be relocated. From the linkers point of view, the
data may be an instruction, in which case it will need to modify only the address portion
of the instruction, or it may be a pointer, in other words an indirect address.
The length of the data. For a data pointer, this is the length of the pointer. For an
instruction, it is the length of the address field of the instruction. Some systems have
strange instruction formats, and this can become quite complicated.
Information about the section in which the data will be located. This could be the current
text or data section, or it could be a reference to an external symbol.
This function has confusing semantics: the symbol table structure struct nlist does not
contain the name of the symbol. Instead, it contains a pointer to the name of the symbol. On
disk, the symbol is located in the string list, but in your program you supply the strings in
advance. For the System V.4 ELF format, the structure is
struct nlist
{
char *n_name; /* name of symbol */
long n_value; /* value of symbol */
short n_scnum; /* section number */
unsigned short n_type; /* type and derived type */
char n_sclass; /* storage class */
char n_numaux; /* number of auxiliary entries */
};
To use the nlist function, you create an array of struct nlist entries with n_name set to
the symbols you are looking for. The last entry contains a null string to indicate the end of the
list. nlist searches the symbol table and fills in information for the symbols it finds, and sets
all fields except n_name to 0 if it cant find the string.
The return value differs from one system to another:
If filename doesnt exist, or if it isnt an object file, nlist returns -1.
If all symbols were found, nlist returns 0.
If some symbols were not found, BSD nlist returns the number of symbols not found.
System V nlist still returns 0.
nm output is frequently used by tools such as shell scripts used during building. This can be a
problem, since the format of the printout depends strongly on the object file format. In the
following sections well look at the differences between nm output for a.out, COFF and ELF
files.
sigsuspend.o:
00000030 T _sigsuspend
U cerror
The lines with the file name and colon tell you the name of the archive member (in other
words, the object file) from which the following symbols come. The other lines contain a
value (which may be missing if it is not defined), a type letter, and a symbol name.
Thats all there is to a.out symbols. As we will see, a.out handles debugging information sep-
arately. On the other hand, this means that the type letters are reasonably easy to remember.
Upper case represents global symbols, lower case represents local symbols. Table 21-2 gives
an overview:
Type Meaning
letter
- symbol table entries (see the -a flag).
A absolute symbol (not relocatable)
B bss segment symbol
C common symbol
D data segment symbol
f file name (always local)
T text segment symbol
U undefined
printf.c | | file | | | |
DGROUP | 0|static| | | |.data
printf | 0|extern| | | |.text
_doprnt | 0|extern| | | |
_iob | 0|extern| | | |
Debugging information
Symbolic debuggers have a problem: they relate to the object file, but they want to give the
impression that they are working with the source file. For example, the program addresses
that interest you are source file line numbers, not absolute addresses, and you want to refer to
variables by their names, not their addresses. In addition, you expect the debugger to know
the types of variables, so that when you ask the debugger to display a variable of type char
*, it displays a string, and when you ask it to display a float, you get the correct numeric
value.
The object file structures we have seen so far dont help too much. Information is available
for global symbols, both in the text and data sections, but the type information is not detailed
enough to tell the debugger whether a data variable is a char * or a float. The symbol ta-
ble information contains no information at all about local variables or line numbers. In addi-
tion, the symbol table information goes away at link time, so it wouldnt be of much help any-
way.
For these reasons, separate data structures for debugging information were introduced. In the
a.out format, they have nothing to do with the rest of the file. In COFF and ELF, they are
more integrated, but debugging information has one thing in common in all object formats: it
is at the end of the file so that it can be easily removed when it is no longer wanted debug-
ging information can become very large. Its not uncommon to see debugging information
increase the size of an executable by a factor of 5 or 10. In extreme cases, such as in libraries,
it can become 50 times as big.
Frequently youll see a make all that creates an executable with debugging symbols, and a
make install that installs the same executable but removes the debugging symbols. This
process is called stripping, and can be done by the program strip or by install with the -s flag.
In order to do this, it makes sense for the debugging information to be in its own section at the
end of the file, and this is how all object file formats solve the problem.
Debugging information is supplied in the assembler source in the form of directives. In
Example 21-2, which is from an assembler designed to create a.out relocatables, this job is
done by the .stabs, .stabn and .stabd directives. These directives are discussed in more
detail in Appendix C, Assembler directives and flags, on page 421. Lets look at the directives
in our example:
At the beginning of the file there are a lot of .stabs directives defining standard data
types, so many that we have omitted most of them. The compiler outputs these directives
even if the data type isnt used in the program, and theyre handy to have in case you
want to cast to this data type when debugging.
Throughout the file you find individual .stabd 68 directives. These specify that the
line number specified in the last parameter starts at this point in the text.
At the end of the function _inc, information about the function itself and the variables
associated with it appear in further .stabs directives.
Finally, information about the block structure of the function appears in the .stabn
directive.
This information is very dependent on the object file format. If you need more information,
the best source is the accompanying system documentation.
The linker
You usually encounter the linker as the last pass of the C compiler. As the name ld implies,
the linker was called the loader in the Seventh Edition, though all modern systems call it a
link editor.* Traditionally, the compiler compiles object files, and then runs the linker to create
an executable program. This is logical, since a single executable is always composed of mul-
tiple object files, whereas there is a one-to-one relationship between source files and object
modules.
The most important function performed by the linker is symbol resolution. To understand
this, we need to define a few terms:
The symbol list, sometimes called a symbol table, is an internal data structure where the
linker stores information about all symbols whose name it has encountered. It contains
the same kind of information about the symbol as we saw in struct nlist on page
363.
An undefined symbol is only partially undefined: we know at least its name, but some
part of its value is unknown.
Initially, the symbol list is empty, but every file that is included adds to the list. A number of
cases can occur:
The file refers to an undefined symbol. In this case, if the linker has not yet seen this
symbol, it enters it into the symbol list and starts a list of references to it. If the symbol
is already in the symbol list, the linker adds a reference to the symbols reference list.
The file refers to a symbol that has already been defined. In this case, the linker simply
performs the required relocation.
The file defines a symbol. There are three possibilities here:
If the symbol has not been encountered before, it is just added to the symbol list.
If the symbol is already marked as undefined, the linker updates the symbol infor-
mation and performs the required relocation for each element in the reference list.
If the symbol is known and defined, it is now doubly defined. The linker prints an
error message, and will not create an output file.
At the same time as it creates the symbol list, the linker copies data and text sections into the
areas it has allocated for them. It copies each individual section to the current end of the area.
The symbol list entries reflect these addresses.
* Properly, the loader was the part of the operating system that loaded a program into memory prior to
execution. Once, long before the advent of UNIX, the two functions were almost synonymous.
Even if you supply only a single object file yourself, you need the C startup code in crt0.o and library
modules from system libraries such as libc.a.
Function libraries
Many of the functions you use in linking an executable program are located in function
libraries, a kind of object file archive built by ar. The linker knows about the format of ar ar-
chives and is able to extract the object files from the archive. The resultant executable con-
tains code from the object files specified on the command line and from the object files found
in the libraries. The functions in the libraries are just like any others you may include. They
run in user context, not kernel context, and are stored in libraries simply for convenience. We
can consider three groups:
The standard* C library, normally /usr/lib/libc.a. This library contains at least the func-
tions needed to link simple C programs. It may also contain functions not directly con-
nected with the C language, such as network interface functionsBSD does it this way.
Additional libraries supporting system functions not directly concerned with the C pro-
gramming language. Networking functions may also fall into this category System V
does it this way.
Libraries supporting third party packages, such as the X11 windowing system.
Library search
You can specify object files to the linker in two different ways: you specify that an object file
is to be included in the output or that a library file is to be searched by specifying its name on
the command line. The library search is one of the most powerful functions performed by the
linker. Instead of including the complete library in the output file, the linker checks each
object file in the library for definitions of currently undefined symbols. If this is the case, it
includes the object file, and not the library. This has a number of implications:
The linker includes only object files that define symbols referenced by the program, so
the program is a lot smaller than it would be if you included the complete library.
We dont want to include anything that isnt required, so each object file usually defines a
single function. In some rare cases, it may define a small number of related functions
that always get included together.
Each object file may refer to other external symbols, so including one file in an archive
may require including another one.
If you compile a library with symbols, each single-function object file will contain
debugging information for all the external information defined in the header files. This
information is usually many times the size of the function text and data.
Once the library has been searched, the linker forgets it. This has important conse-
quences which well examine on page 373.
For reasons shrouded in history, you dont specify the path name of the library fileinstead
* Note the lower-case use of the word standard. Whether or not the library conforms to the ANSI/ISO
C Standard, it is a standard part of a software development system.
you tell the linker the names of directories that may contain the libraries you are looking for,
and a coded representation of the library name. For example, if you want to include
/opt/lib/libregex.a in your search path, you would include -L/opt/lib -lregex in your
compiler or linker call:
-L/opt/lib tells the linker to include /opt/lib in the list of directories to search.
-lregex tells the linker to search for the file libregex.a in each of the directories to
search.
This can be a problem if you have four files /usr/lib/libfoo.a, /usr/lib/libbar.a, /opt/lib/libfoo.a
and /opt/lib/libbar.a, and you want to search only /opt/lib/libfoo.a and /usr/lib/libbar.a. In this
case, you can name the libraries explicitly.
To keep the pain of linking executables down to tolerable levels, the compiler control program
(usually cc) supplies a few library paths and specifications for freenormally the equivalent
of -L/usr/lib -lc, which at least finds the library /usr/lib/libc.a, and also supplies the path
to all other libraries in /usr/lib. You need only specify additional paths and libraries. Occa-
sionally this behaviour is undesirable: what if you deliberately want to exclude the standard
libraries, like if youre building an executable for a different version of the operating system?
Some compilers give you an option to forget these libraries. For example, in gcc it is -nost-
dlib.
Like most aspects of UNIX, there is no complete agreement on where to store library files, but
most systems come close to the following arrangement:
/usr/lib contains the basic system libraries as well as startup code like crt0.o and friends,
which are bound in to supply low-level support for the C language. Well look at this in
the next section.
Some of these files used to be stored in /lib. Nowadays /lib tends either not to be present
or, for compatibilitys sake, it is a symlink to /usr/lib.
System V.4 systems place BSD compatibility libraries in /usr/ucblib*. Many of these
functions duplicate functions in /usr/lib.
/usr/X11/lib, /usr/X/lib, /usr/lib/X11, /usr/lib/X11R6 and others are some of the places
that the X11 libraries might be hidden. This directory probably contains all the parts of
X11 and related code that are on your system.
Shared libraries
Some libraries can be very big. The X11R6 version libX11.a, the standard X11 functions,
runs to 630 kB on BSD/OS. The Motif library libXm.a is nearly 1.4 MB in size. This can
lead to enormous executables, even if the program itself is relatively smallthe 500 kB
Hello world syndrome. Since these functions are used in many programs, many copies of a
function may be active at any one time in the system. For example, just about every program
* UCB stands for the University of California at Berkeley, the home of the Berkeley Software Distribu-
tions. Youll frequently find BSD-derived software stored in directories whose names start with the let-
ters ucb.
uses the function printf, which with its auxiliary functions can be quite big. To combat this,
modern UNIX flavours support shared libraries: the library itself is no smaller, but it is in
memory only once.
Two different library schemes are in current use: static shared libraries* and dynamic shared
libraries. Static shared libraries contain code which has been linked to run at a specific
address, which means that you could have difficulties if your program refers to two libraries
with overlapping address ranges, or if you use a different version of the library with functions
at slightly different addresses. Dynamic libraries get round this problem by linking at run
time, which requires a dynamic linker. Unless youre building shared libraries, a topic beyond
the scope of this book, you dont need to worry about the difference between the two. If you
do find yourself in the situation where you need to build shared libraries, your best source of
information is your operating system documentation.
A shared library performs two different functions:
When you link your program, it supplies information about the locations of the functions
and data in the library. Some systems, such as SunOS 4, supply a stub file with a name
like libc.sa.1.9. Since it does not contain the code of the functions, it is relatively
small on SunOS 4.1.3, it is 7996 bytes long. Other systems, such as System V.4, only
supply a single library file with a name like libc.so. The linker only includes enough
information for the dynamic loader to locate the functions in the library file at run time.
At run time, it supplies the functions and data. On all systems, the file name is of the
form libc.so.1.9
Its important to ensure that you use the same library to perform these two actions. If a func-
tion or a data structure changes between versions of the library, a program written for a differ-
ent version may work badly or crash. This is a common problem: most programs are distrib-
uted in executable form, and thus contain preconceived notions about what the library looks
like. Since were linking the program ourselves, we should not run in to this problem. If you
do run into problems, you can always fall back to static (unshared) libraries.
* Dont confuse static shared libraries with the term static libraries, which are traditional, non-shared
libraries.
/opt/lib/gcc-lib/i386-unknown-sysv4.2/2.5.8/crtend.o
/usr/ccs/lib/crtn.o -lgcc
The same example in BSD/OS specifies the files /usr/lib/crt0.o, foo.o, -lbar, -lbaz, -lgcc, -lc
and -lgcconly fractionally more readable. This example should make it clear why almost
nobody starts the linker directly.
This links the three object files foo.o, bar.o and baz.o and creates a new object file foobarbaz.o
that contains all the functions and data in the three input files.
Here are four different kinds of object files in the same directory. Occasionally, you will see
files like this that are there for a good reason: due to license reasons, there are no correspond-
ing sources, and there will be one object for each architecture that the package supports. In
this example, however, the file names are different enough that you can be reasonably sure
that these files are junk left behind from previous builds. If the object files are still there after
a make clean, you should remove them manually (and fix the Makefile).
Missing functions
The UNIX library mechanism works well and is reasonably standardized from one platform to
the next. The main problem you are likely to encounter is that the linker cant find a function
that the program references. There can be a number of reasons for this:
The symbol may not be a function name at all, but a reference to an undefined preproces-
sor variable. For example, in xfm version 1.1, the source file FmInfo.c contains:
if (S_ISDIR(mode))
type = "Directory";
else if (S_ISCHR(mode))
type = "Character special file";
else if(S_ISBLK(mode))
type = "Block special file";
else if(S_ISREG(mode))
type = "Ordinary file";
else if(S_ISSOCK(mode))
type = "Socket";
else if(S_ISFIFO(mode))
type = "Pipe or FIFO special file";
sys/stat.h defines the macros of the form S_ISfoo. They test the file mode bits for spe-
cific file types. System V does not define S_ISSOCK (the kernel doesnt have sockets), so
The function is in a different library, and you need to specify it to the linker. A good
example is the networking code we mentioned on page 369: a reference to socket will
link just fine with no additional libraries on a BSD platform, but on some versions of
System V.3 you will need to specify -linet, and on System V.4 and other versions of
System V.3 you will need to specify -lsocket. The findf script can help here. It uses
nm to output symbol information from the files specified in LIBS, and searches the out-
put for a function definition whose name matches the parameter supplied. The search
parameter is a regular expression, so you can search for a number of functions at once.
For example, to search for strcasecmp and strncasecmp, you might enter:
$ findf str.*casecmp
/usr/lib/libc.a(strcasecmp.o): _strcasecmp
/usr/lib/libc.a(strcasecmp.o)): _strncasecmp
/usr/lib/libc_p.a(strcasecmp.po): _strcasecmp
/usr/lib/libc_p.a(strcasecmp.po)): _strncasecmp
Because of the differences in nm output format, findf looks very different on BSD sys-
tems and on System V. You may find that you need to modify the script to work on your
system. Example 21-3 shows a version for 4.4BSD:
Example 213:
LIBS="/usr/lib/lib* /usr/X11R6/lib/lib*"
nm $LIBS 2>/dev/null \
| awk -v fun=$1 \
/\// {file = $1};
/[\/].*:/{member = $1};
$3 fun && $2 /T/ {
sub (":$", "", file); ; sub (":$", "):", member); print file "(" member "\t" $3}
On a system like System V.4, which uses ELF format, the corresponding script is in
Example 21-4:
Example 214:
LIBS="/usr/lib/lib* /usr/X11R6/lib/lib*"
nm $LIBS 2>/dev/null \
| sed s:|: :g \
| gawk -v fun=$1 \
/Symbols from/ {file = $3};
$8 fun && $4 /FUNC/ { print file member "\t" $8 }
bar.c
void bar (char *c)
{
baz (c);
}
baz.c
void baz (char *c)
{
puts (c);
}
We compile them to the corresponding object files, and then make libraries libbar.a and lib-
baz.a, which contain just the single object file bar.o and baz.o respectively. Then we try to
link:
$ gcc -c foo.c
$ gcc -c bar.c
$ gcc -c baz.c
$ ar r libbaz.a baz.o
$ ar r libbar.a bar.o
$ gcc -o foo foo.o -L. -lbaz -lbar
Undefined first referenced
symbol in file
baz ./libbar.a(bar.o)
ld: foo: fatal error: Symbol referencing errors. No output written to foo
$ gcc -o foo foo.o -L. -lbar -lbaz
$
In the first link attempt, the linker included foo.o, then searched libbaz.a and didnt find any-
thing of interest. Then it went on to libbar.a and found it needed the symbol baz, but by that
time it was too late. You can solve the problem by putting the reference -lbar before -lbaz.
This problem is not even as simple as it seems: although its bad practice, you sometimes find
that libraries contain mutual references. If libbar.a also contained an object file zot.o, and baz
referred to it, you would have to link with:
$ gcc -o foo foo.o -L. -lbar -lbaz -lbar
An alternative seems even more of a kludge: with the -u flag, the linker will enter an unde-
fined symbol in its symbol table. In this example, we could also have written
$ gcc -u baz -o foo foo.o -L. -lbaz -lbar
$
executable dumps, but the dumped executable does not have a format that the kernel can rec-
ognize.
Other programs that use this technique are gcl (GNU common LISP) and TEX.
Stack frames
Most modern machines have a stack-oriented architecture, even if the support is rather rudi-
mentary in some cases. Everybody knows what a stack is, but here well use a more restric-
tive definition: a stack is a linear list of storage elements, each relating to a particular function
invocation. These are called stack frames. Each stack frame contains
The parameters with which the function was invoked.
The address to which to return when the function is complete.
Saved register contents.
Variables local to the function.
The address of the previous stack frame.
With the exception of the return address, any of these fields may be omitted.* Typical stack
implementations supply two hardware registers to address the stack:
The stack pointer points to the last used word of the stack.
The frame pointer points to somewhere in the middle of the stack frame.
The resultant memory image looks like:
* Debuggers recognize stack frames by the frame pointer. If you dont save the frame pointer, it will
still be pointing to the previous frame, so the debugger will report that you are in the previous function.
This frequently happens in system call linkage functions, which typically do not save a stack linkage, or
on the very first instruction of a function, before the linkage has been built. In addition, some optimizers
remove the stack frame.
Function arguments
Return address
Old value of frame pointer
Automatic variables
Stack frame 0
Temporary storage
Return address
Old value of frame pointer
Frame pointer
Automatic variables
Temporary storage
Stack pointer
The individual parts of the stack frames are built at various times. In the following sections,
well see how the stack gets set up and freed.
This structure is supplied for convenience and is not strictly necessary. Many systems, for
example FreeBSD, do not define it.
Next, exec places on the stack all environment variable strings, followed by all the program
arguments. Some systems severely limit the maximum size of these stringswe looked at
the problems that that can cause in Chapter 5, Building the package, page 74.
After the variable strings come two sets of NULL-terminated pointers, the first to the environ-
ment variables, the second to the program arguments.
Finally comes the number of arguments to main, the well-known parameter argc. At this
point, the stack looks like:
ps information
Environment variables
Program arguments
NULL
Environment pointers
NULL
more argument pointers
argv [1]
argv [0]
argc
Stack pointer
At this point, all the data for main is on the stack, but its not quite in the form that main
needs. In addition, theres no return address. But where could main return to? All this work
has been done in the kernel by exec: we need to return to a place in the program. These prob-
lems are solved by the function start, the real beginning of the program: it calls main, and
then calls exit with the return value from main. Before doing so, it may perform some run-
time initializations. A minimal start function looks like this stripped down version of GNU
libc start.c, which is the unlikely name of the source file for crt0.o:
static void start (int argc, char *argp)
{
char **argv = &argp; /* set up a pointer to the first argument pointer */
__environ = &argv [argc + 1]; /* The environment starts just after argv */
asm ("call L_init"); /* call the .init section */
__libc_init (argc, argv, __environ); /* Do C and C++ library initializations */
exit (main (argc, argv, __environ)); /* Call the user program */
}
The asm directive is used for C++ initializationwell look at that on page 380. But whats
this? start calls main with three parameters! The third is the address of the environment
variable pointers. This is one of the best kept secrets in UNIX: main really has three parame-
ters:
int main (int argc, char *argv [], char *envp []);
It isnt documented anywhere, but its been there at least since the Seventh Edition and its
unlikely to go away, since there isnt really any other good place to store the environment
variables.
By the time we have saved the stack linkage in main, the top of the stack looks like:
*argv [0]
argc
dummy return
start stack frame
dummy frame pointer
**environ
main stack frame
**argv
argc
return to start
Frame pointer
saved frame pointer
Stack pointer
The assembler code for the calling sequence for foo in main is:
pushl -4(%ebp) value of x
pushl -8(%ebp) value of y
call _foo call the function
addl $8,%esp and remove parameters
Register ebp is the base pointer, which we call the frame pointer. esp is the stack pointer.
The push instructions decrement the stack pointer and then place the word values of x and y
at the location to which the stack pointer now points.
The call instruction pushes the contents of the current instruction pointer (the address of the
instruction following the call instruction) onto the stack, thus saving the return address, and
loads the instruction pointer with the address of the function. We now have:
argc
return to start
saved frame pointer
Frame pointer
local var x
main stack frame
local var y
parameter a
foo stack frame
parameter b
return to main
Stack pointer
The called function foo saves the frame pointer (in this architecture, the register is called ebp,
for extended base pointer), and loads it with the current value of the stack pointer register esp.
The frame pointer isnt absolutely necessary: you can get by without it and refer to the stack
pointer instead. The problem is that during the execution of the function, the compiler may
save further temporary information on the stack, so its difficult to keep track of the value of
the stack pointerthats why most architectures use a frame pointer, which does stay con-
stant during the execution of the function. Some optimizers, including newer versions of gcc,
give you the option of compiling without a stack frame. This makes debugging almost impos-
sible.
On return from the function, the sequence is reversed:
movl -12(%ebp),%ebx and restore register ebx
leave reload ebp and esp
ret and return
The first instruction reloads the saved register ebx, which could be stored anywhere in the
stack. This instruction does not modify the stack.
The leave instruction loads the stack pointer esp from the frame pointer ebp, which effectively
discards the part stack below the saved ebp value. Then it loads ebp with the contents of the
word to which it points, the saved ebp, effectively reversing the stack linkage. The stack now
looks like it did on entry.
Next, the ret instruction pops the return address into the instruction pointer, causing the next
instruction to be fetched from the address following the call instruction in the calling function.
The function parameters x and y are still on the stack, so the next instruction in the calling
function removes them by adding to the stack pointer:
addl $8,%esp and remove parameters
385
C compiler options
-a (gcc, SunOS)
Generate extra code to write profile information for tcov.
-a align (some MS-DOS compilers)
Align in structs to align boundary.
-A (SVR3)
Linker output should be an absolute file (i.e. the opposite of the -r option).
-A (gcc, SVR4)
-Aquestion(answer) asserts that the answer to question is answer. This can used with the pre-
processor conditional #if #question(answer).
391
-dy (SVR4)
Use dynamic linking where possible. This is the default.
-E (all)
Write preprocessor output to standard output, then stop. Some compilers interpret the -o
option and write the output there instead if specified.
-EP (SCO UNIX, XENIX)
Use this instead of the -E option to generate preprocessor output without #line directives. The
output is written to standard output. In addition, SCO UNIX copies the output to a file with the
suffix .i.
-F num (SCO UNIX, XENIX)
Set the size of the program stack to num (hexadecimal) bytes.
-f (gcc)
A family of options specifying details of C dialect to be compiled. See page 405 for more
details.
-f type (SunOS)
Specify the kind of floating-point code to generate on Sun-2, Sun-3 and Sun-4 systems.
-Fa name (SCO UNIX, XENIX)
Write an assembler source listing to name (default file.s).
-Fc name (SCO UNIX, XENIX)
Write a merged assembler and C source listing to name (default file.L).
-feedback name (SGI)
Specify the name of the feedback file used in conjunction with the -cord option.
-Fe name (SCO UNIX, XENIX)
Specify the name of the executable file.
-Fl name (SCO UNIX, XENIX)
Write an assembler listing with assembler source and object code to name (default file.L).
-float (SGI)
Cause the compiler not to promote float to double.
-Fm name (SCO UNIX, XENIX)
Write a load map to name (default a.map).
-Fo name (SCO UNIX, XENIX)
Specify the name of the object file.
-Fp (SCO UNIX, XENIX)
Specify floating point arithmetic options for MS-DOS cross-compilation.
-framepointer (SGI)
Use a register other than the stack pointer (sp) for the frame pointers (see Chapter 21, Object
files and friends, page 377).
-fullwarn(SGI)
Produce all possible warnings.
-Fs name (SCO UNIX, XENIX)
Write a C source listing to name (default file.S).
-G (SVR4)
Instruct the linker to create a shared object rather than a dynamically linked executable. This is
incompatible with the -dn option.
-G size (SGI)
Limit items to be placed in the global pointer area to size bytes.
-g (all)
Create additional symbolic information in order to support symbolic debuggers. gcc has a
number of suboptions to specify the amount and the nature of the debugging informationsee
page 406 for more details. SGI C specifies a numeric level for the amount of debug informa-
tion to produce.
-Gc (SCO UNIX)
Generate code with the alternate calling sequence and naming conventions used in System V
386 Pascal and System V 386 FORTRAN.
-go (SunOS)
Produce additional symbol table information for adb.
-Gs (SCO UNIX)
Removes stack probe routines. Effective only in non-protected environments.
-H (gcc, System V)
Print the names of header files to the standard output as they are #included.
-H num (SCO UNIX, XENIX)
Set the maximum length of external symbols to num.
-help (SCO UNIX, SunOS)
Display help for cc.
-I dir (all)
Add dir to a list of pathnames to search for header files included by the #include directive.
-I (SGI)
Remove /usr/include from the list of paths to search for header files.
-I- (gcc)
Search the list of include pathnames only when the #include directive is of the form #include
header". Do not search these directories if the directive is #include <header>. In addition, do
not search the current directory for header files. If -I dir options are specified after -I-,
they apply for all forms of the #include directive.
-i (SCO UNIX, XENIX)
Create separate instruction and data spaces for small model programs.
-J (SCO UNIX)
Change the default mode for the char type to unsigned.
-J (SunOS, Sun-2 and Sun-3)
Generate 32-bit offsets in switch statements.
-J sfm (SVR4)
Specify the pathname of the assembly language source math library libsfm.sa. The positioning
of this option is important, since the library is searched when the name is encountered.
-j (SGI)
Create a file file.u containing intermediate code. Does not create an object file unless used in
conjunction with -c.
-KPIC (SGI)
Generate position-independent code.
-imacros file (gcc)
Process file before reading the regular input. Do not produce any output for fileonly the
macro definitions will be of use.
-include file (gcc)
Process file as input before processing the regular input file. The text of the file will be handled
exactly like the regular files.
-K (SVR4)
Specify various code generation options.
-K (SCO UNIX, XENIX)
Remove stack probes from a program. Useful only in non-protected environments.
-k options (SGI)
Pass options to the ucode loader.
-ko name (SGI)
Cause the output of the intermediate code loader to be called name.
-L (SCO UNIX, XENIX)
Create an assembler listing with assembled code and assembler source instructions with the
name file.L.
-L dir (All but SCO UNIX, XENIX)
Add dir to the list of directories to search to resolve library references. See Chapter 18, Func-
tion libraries, page 369 for further details.
-l (all but XENIX)
Specify a library. The option -lbaz will search the library paths specified via -L options (see
above) for a file typically called libbaz.a. See Chapter 18, Function libraries, page 369 for
more details.
-prototypes (SGI)
Output ANSI function prototypes for all functions in the source file when run in -cckr mode.
-qp (System V)
A synonym for -p.
-Qn (gcc (System V versions), SVR4)
Do not output .ident directives to the assembler output to identify the versions of each tool
used in the output file.
-Qy (gcc (System V versions), SVR4)
Output .ident directives to the assembler output to identify the versions of each tool used in the
output file.
-Qprog opt (SunOS)
Pass option opt to program prog. prog may be as (the assembler), cpp (the preprocessor),
inline (the assembly code reorganizer) or ld (the loader).
-Qpath (SunOS)
Specify search paths for compiler passes and other internal files, such as *crt*.o.
-Qproduce type (SunOS)
Produce source code output of type type. type specifies the filename extension and may be one
of .c (C source), .i (preprocessor output), .o (object output from the assembler) or .s (assembler
output from the compiler).
-R (SunOS)
Merge the data segment into text. This creates read-only data.
-r (SCO UNIX, XENIX)
Invoke the incremental linker /lib/ldr for the link step.
-r (SVR3)
Instruct the linker to retain relocation information in the final executable.
-S (gcc, SGI, SunOS, System V)
Stop after compiling the output assembler code, and do not assemble it. Save the results in a
file file.s.
-S (SCO UNIX, XENIX)
Create a human-readable assembler source listing in file.s. This listing is not suitable for
assembly.
-s (SCO UNIX, XENIX, SVR3)
Strip the final executable.
-save-temps (gcc)
Keep intermediate files even when they are no longer needed.
-sb (SunOS)
Generate additional symbol table information for the Sun Source Code Browser.
Enable trigraph processing. By default, trigraphs are disabled unless the -ansi option is spec-
ified.
-U macro (all)
Undefine macro.
-u symbol (gcc, SVR3)
Force the linker to resolve the symbol symbol by searching additional libraries where speci-
fied.
-u (SCO UNIX)
Undefine all predefined macros.
-undef (gcc)
Do not predefine standard macros. This includes the macros which define the architecture.
-use-readonly-const(SGI)
Do not allow writing to strings and aggregate constants.
-use-readwrite-const(SGI)
Allow writing to strings and aggregate constants.
-V (System V)
Print version numbers of the compiler passes as they are invoked.
-V version (gcc 2.X)
Tell gcc to run version version of gcc.
-V"string" (SCO UNIX)
Place string in the object file, typically for use as a copyright notice or version information.
-V version (XENIX)
Compile a program compatible with specific versions of UNIX. version may be 2 (Seventh
Edition compatible), 3 (System III compatible) or 5 (System V compatible).
-v (gcc, SGI)
Produce verbose output. gcc output includes the complete invocation parameters of each pass
and the version numbers of the passes.
-v (SVR4)
Perform more and stricter semantic checks.
-varargs (SGI)
Print warnings for lines that may requires the varargs.h macros.
-W (gcc)
Without print a number of additional warning messages. With an argument, add a specific kind
of warning message checksee page 407 for more details.
-W num (SCO UNIX, XENIX)
Specify the level of warning messages. If num is 0, no warnings are produced. A maximum
number of warnings is produced by -W3.
-W0,option (System V)
Pass option to the compiler.
-W2,option (System V)
Pass option to the optimizer.
-Wa,option (gcc, System V)
Pass option to the assembler.
-Wb,option (System V)
Pass option to the basic block analyzer.
-Wl,option (gcc, System V)
Pass option to the linker.
-Wp,option (System V)
Pass option to the preprocessor.
-w (gcc, SCO UNIX, SunOS, XENIX)
Inhibit warning messages.
-w num (SGI)
If num is 0 or 1, suppress warning messages. If num is 2, treat warnings as errors.
-wline (SGI)
Produce lint-like warning messages.
-Yp,dir (SVR3)
Search for compiler in directory dir.
-YS,dir (SVR3)
Search for startup files crt1.o and crtend.o in directory dir.
-YU,dir (SVR3)
Search for second default library directory in directory dir.
-z (SCO UNIX, XENIX)
Display the passes and arguments, but do not execute them.
-z (SVR3)
Instruct the linker not to bind anything at address 0 to aid run-time detection of null pointers.
-Za (SCO UNIX, XENIX)
Restrict the language to ANSI specifications.
-Zd (SCO UNIX, XENIX)
Include line number information in the object file.
-Ze (SCO UNIX)
Enables the keywords far, near, huge, pascal and fortran keywords. The same as
the -Me option.
-Zi (SCO UNIX, XENIX)
Include symbolic information in the object file.
-Zl (SCO UNIX)
Do not include default library information in the object file.
-Zpalign (SCO UNIX, XENIX, SVR3)
Force structs to align to the an align boundaries. align may be 0, 2 or 4, and defaults to 1.
-Zs (SCO UNIX, XENIX)
Perform syntax check only, do not compile.
-ansi
Compile ANSI C. Flag any non-standard extension as warnings, but do not treat them as
errors. This option implies the options -fn-asm and -trigraphs.
-fno-asm
Do not recognize the keywords asm, inline or typeof, so that they can be used as
identifiers. The keywords __asm__, __inline__ and __typeof__ can be used instead.
-fno-builtin
Dont recognize builtin function names that do not begin with two leading underscores.
-trigraphs
Support ANSI C trigraphs.
-traditional
Support pre-ansi dialects. This also implies -funsigned-bitfields and -fwritable-
strings.
-traditional-cpp
Provide pre-ANSI style preprocessing. This is implied by -traditional.
-fcond-mismatch
Allow conditional expressions (such as a: b? c) where the second and third arguments have
different types.
-funsigned-char
By default, characterss are unsigned. This effectively makes the declaration char the same
thing as unsigned char.
-fsigned-char
By default, characterss are signed. This effectively makes the declaration char the same thing
as signed char.
-fsigned-bitfields
Make bit fields signed by default. This is the default action.
-funsigned-bitfields
Make bit fields unsigned by default.
-fno-signed-bitfields
Make bit fields unsigned by default.
-fno-unsigned-bitfields
Make bit fields signed by default. This is the default action.
-fwritable-strings
Allocate strings in the data segment, so that the program can write to them. See Chapter 20,
Compilers, page 338 for a discussion of this misfeature.
-fallow-single-precision
Do not perform operations on single precision floating point values with double precision
arithmetic. This is only needed if you specify -traditional.
not used.
-Wswitch
Warn if a switch statement has an index of an enumeral type and does not cater for all the
possible values of the enum, or if a case value is specified which does not occur in the enum.
-Wcomment
Warn if the sequence /* is found within a comment. This might mean that a comment end is
missing.
-Wtrigraphs
Warn if trigraphs are encountered. Only effective if -ftrigraphs is also specified.
-Wformat
Check the parameters supplied to printf, scanf and friends to ensure that they agree with
the format string.
-Wchar-subscripts
Warn if an array subscript has type char.
-Wuninitialized
Warn if an automatic variable is used before it is initialized. This requires the optimizer to be
enabled.
-Wparentheses
Warn if parentheses are omitted in assignments in contexts where truth values are expected
(for example, if (a = foo ()), or when unusual and possibly confusing sequences of
nested operators occur without parentheses.
-Wenum-clash
Warn if enum types are mixed. This is only issued for C++ programs. See Chapter 20, Com-
pilers, page 339 for further details.
-Wtemplate-debugging
Warn if debugging is not fully available for the platform when using templates in a C++ pro-
gram.
-Wall
Specify all of the warning options above. The FSF considers this a good compromise between
accuracy and completeness.
-fsyntax-only
Check for syntax errors, but dont compile.
-pedantic
Issue all warnings specified by ANSI C. Reject programs which use extensions not defined in
the Standard. The Free Software Foundation does not consider this to be a useful option, since
ANSI C does not specify warnings for all possible situations. It is included because it is
required by the ANSI Standard.
-pedantic-errors
The same thing as -pedantic, but the warnings are treated as errors.
-w
Inhibit all warning messages.
-Wno-import
Inhibit warning messages about the use of #import.
-Wtraditional
Warn about: Macro parameters in strings, functions declared external within a block and then
referenced outside the block and switch statements with long indexes. These are treated
differently in ANSI and traditional C.
-Wshadow
Warn if a local variable shadows another local variable.
-Wid-clash-len
Warn whenever two different identifiers match in the first len characters. To quote the FSF
documentation: This may help you prepare a program that will compile with certain obsolete,
brain-damaged compilers.
-Wpointer-arith
Warn about anything that depends on the size of a function type or of void. GNU C
assigns these types a size of 1, for convenience in calculations with void * pointers and
pointers to functions.
-Wcast-qual
Warn when a cast removes a type qualifier from a pointer, for example if a const char * is
cast to a char *.
-Wcast-align
Warn if a pointer is cast to a type which has an increased alignment requirement. For example,
warn if a char * is cast to an int * on machines where integers require specific align-
ments.
-Wwrite-strings
Give string constants the type const char []. This will cause a warning to be generated if
a string address is copied into a non-const char * pointer.
-Wconversion
Warn if the existence of a prototype causes a different type conversion from the default, or if a
negative integer constant expression is implicitly converted to an unsigned type.
-Waggregate-return
Warn when functions that return structures, unions or arrays are defined or called.
-Wstrict-prototypes
cpp options
-$ (gcc)
Disable the use of the character $ in identifers. This is passed by gcc when the -ansi option
is specified.
-A (gcc)
-Aquestion(answer) asserts that the answer to question is answer. This can used with the pre-
processor conditional #if #question (answer).
-A- (gcc)
Disable standard assertions. In addition, SVR4 cc undefines all standard macros except those
beginning with __.
-B (SunOS, Solaris)
Recognize the C++ comment string //.
-C (gcc, SVR3, SunOS, Solaris, XENIX)
Do not strip comments from the preprocessor output.
-Dname (gcc, SVR3, SunOS, Solaris, XENIX)
Define name as 1. This is the equivalent to specifying -Dname=1 to cc and not the same as
-Dname.
as options
Its particularly evident that as seldom sees the light of day when you look at the options,
which differ greatly from one system to the next. GNU as doesnt even maintain compatibil-
ity between versions 1 and 2, as you can see in the following table:
-a (GNU 2.x)
List high-level language, assembly output, and symbols. This is the generic form of the -a
option; the following variants modify this in some manner. Combinations are possible: for
example, -alh lists the high-level input and assembly output, but not the symbol table. In
order to get the high-level language input, you also need to specify the -g option.
-D (GNU 1.x)
Turn on assembler debugging (if available).
-D (GNU 2.x)
No effect just for compatibility.
-dl (SVR3)
415
-f (GNU 2.x)
skip preprocessing (for compiler output)
-g (GNU 1.x)
Generate debugging symbols for source language debugging of assembly programs.
-K (GNU 2.x)
Issue warnings when difference tables altered for long displacements.
-k (GNU 1.x)
Warn about problems with calculating symbol differences.
-L (GNU)
Keep local symbols starting with L in the symbol table output to object file.
-m (System V)
preprocess with m4
-n (System V)
Turn off long/short address optimization.
-Qy (System V)
Put assembler version number in object file.
-R (GNU 1.x)
Merge the data segment into the text segment, making it read-only.
-R (System V)
Remove the input file after assembly.
-W (GNU 1.x)
Suppress warnings.
-f (GNU 1.x)
Suppress the preprocessor pass which removes comments and redundant white space from the
input. This can also be done with the #NO_APP directive.
-T (System V)
Accept (and ignore) obsolete directives without complaining.
-V (System V)
Print the current version number.
-v (GNU)
Print the current version number.
-W (GNU 2.x)
Suppress warning messages
-Y (System V)
Specify directory for m4 processor and predefined macros (Y,dir).
-Yd (System V)
Specify directory for predefined macros (Yd,dir).
-Ym (System V)
Specify directory for m4 processor (Ym,dir).
as directives
Assembler directives are mainly provided for the convenience of the compiler, and are seldom
documented. Here is a list of the directives provided by GNU as, one of the few which is doc-
umented. Many of these directives are provided only on certain platformsread Using as, by
Dean Elsner and Jay Fenlason, for specific information.
.abort
Abort the assembly. This is obsolescent. It was intended to be used by a compiler piping its
output into the assembler when it discovered a fatal error.
.ABORT
A synonym for .abort.
.app-file string
Specify the start of a new logical file string. This is obsolescent.
.ascii string . . .
Emit each string into consecutive addresses. Do not append a trailing \0 character.
.asciz string
Emit each string into consecutive addresses. Append a trailing \0 character.
.byte expressions
Emit zero or more expressions into the next output byte.
.data subsection
Switch to data section subsection (default zero). All assembled data will go to this section.
.def name
Begin defining COFF debugging information for a symbol name. The definition is completed
by a .endef directive.
.double flonums
Emit double floating point number flonums.
.eject
Force a page break in the assembly listing at this point.
.else
else in conditional assemblysee the .if directive.
.endef
End a symbol definition begun with .def.
.endif
End a conditional assembly block. See the .if directive.
.extern
In some assemblers, define a symbol external to the program. This is ignored by GNU as,
which treats all undefined symbols as external.
.file string
Specify the start of a new file. This directive is obsolescent, and may not be available.
.float flonums
Emit floating point numbers flonums.
.global symbol
.globlsymbol
A synonym for .global.
.hword expressions
Emit the values of each expression, truncated to 16 bits if necessary.
.ident
This directive is used by some assemblers to place tags in object files. GNU as ignores it.
.if expression
If expression evaluates to non zero, assemble the following code down to the corresponding
.else or .endif directive. If the next directive is .else, do not assemble the code
between the .else and the .endif. If expression evaluates to 0, do not assemble the code
down to the corresponding .else or .endif directive.
.ifdef symbol
Like .if, but the condition is fulfilled if symbol is defined.
.ifndef symbol
Like .if, but the condition is fulfilled if symbol is not defined.
.ifnotdef symbol
Like .if, but the condition is fulfilled if symbol is not defined.
.include file"
Process the source file file before continuing this file.
.int expressions
Emit 32 bit values of each expression.
.ln line-number
Change the logical line number of the next line to line-number. This corresponds to the C pre-
processor line directive.
.ln line-number
A synonym for .line.
.list
Increment the listing counter (initially 0). If the listing counter is > 0, the following lines will
be listed in the assembly listing, otherwise they will not. .nolist decrements the counter.
.long expressions
A synonym for .int.
.nolist
Decrement the listing countersee .list.
.octa bignums
Evaluate each bignum as a 16 byte integer and emit its value.
.quad bignums
Evaluate each bignum as an 8 byte integer and emit its value.
.sbttl subheading
Set the subtitle of assembly listings to subheading.
.single flonums
Emit floating point numbers flonums. This is the same as .float.
.space
Usually a synonym for .block, but on some hardware platforms GNU as uses it differently.
.stabd
Emit debug information. See page for more information.
.stabn
Emit debug information. See page for more information.
.stabs
Emit debug information. See page for more information.
.text subsection
Switch to text section subsection (default zero). All assembled data will go to this section.
.title heading
Set the title of the assembly listing to heading.
.word expressions
Emit 32 bit values of each expression.
Debug information
Debug information is very dependent on the kind of object file format in use: In a.out format,
it is defined by the directives .stabd, .stabn and .stabs. They can take up to five parame-
ters:
desc is the symbol descriptor, and is 16 bits wide.
other is the symbols other attribute. This is normally not used.
string is the name of the symbol.
type is the symbol type, and is 8 bits wide.
value is the value of the symbol, and must be absolute.
These symbols are used as follows:
.stabd type, other, desc
Define a debugging entry without a name. The value of the symbol is set to the current value
of the location counter. This is commonly used for line number information, which is type 68
for line number references in the text segment. For example .stabd 68, 0, 27 specifies
that the current location is the beginning of line 27.
For further information about stabs formats and types, see the header file stab.h and the man
page stab(5).
In COFF format, it is defined by the directives .dim, .scl, .size, .tag, .type and .val.
They are enclosed in a .def/.endef pair. For example, to define a symbol foo, you would
write
.def foo
.value bar
.size 4
.endef
.dim
Set dimension information.
.scl class
Set the storage class value of the symbol to class.
.size size
Set the size of the symbol to size.
.tag structname
Specify the struct definition of the current symbol.
.type int
Set the type of the symbol to type.
.val addr
Set the value of the symbol to addr.
In ELF format, debug information is output to a special section called .debug, so no specific
directives are needed.
423
binding at link time), or symbolic (force symbolic relocation). Solaris 2 does not support the
keyword nosymbolic.
-Bstatic (SunOS 4, GNU)
Specify static libraries only. GNU ld accepts this option, but ignores it.
-B number (XENIX)
Set the text selector bias to number
-b (SVR4)
When performing dynamic linking, do not perform special processing for relocations to sym-
bols in shared objects.
-b format (new GNU)
Specify the binary format of the files whose names follow. This is only needed when linking
files with multiple formats.
-C (XENIX)
Ignore the case of the symbols.
-c file (new GNU)
Read commands from file. These commands override the standard link format.
-c x (XENIX)
Specify the target CPU type 80x86. x defaults to 3.
-D size (old GNU, SunOS 4)
Pad the data segment to size. The padding may overlap with the bss segment. The SunOS 4
linker interprets size in hexadecimal.
-D number (XENIX)
Set the data selector bias to number.
-dyn (SVR4)
Specify dynamic (yn is y) or static (yn is n) linking.
-d (GNU, SunOS 4)
When creating a relocatable output file with the -r option, convert common symbols to bss.
-dc (SunOS 4)
Perform the -d option, but also copy initialized data referenced by this program from shared
objects.
-dp (SunOS 4)
Force an alias definition of undefined procedure entry points. Used with dynamic binding.
-defsym symbol = expression (new GNU)
Create the global symbol symbol in the output file and assign the value expression to it.
-e symbol (all)
Set the entry address in the output file to symbol.
Search the specified libraries for a library called liblib.a. This is the same as the C compiler
-l option. SunOS4 allows you to write -l lib.version to indicate a specific
library version number.
-La (XENIX)
Set advisory file locking
-Lm (XENIX)
Set mandatory file locking.
-LI[NENUMBERS] (SCO)
Create a map file including line number information.
-M (GNU, SunOS 4)
Print a load map on the standard output.
-M mapfile (Solaris 2)
Read directives to ld from mapfile.
-M (SCO, SVR3)
Print warning messages for multiply defined external definitions.
-m (SCO, SVR3, SVR4)
Print a load map on the standard output.
-Mx (XENIX)
Specify the memory model. x can be s (small), m (middle), l (large), h (huge), or e (mixed).
-m emulation (new GNU)
Emulate the emulation linker.
-m file (XENIX)
Write a map listing to file.
-M[AP]:number (SCO)
Create a map listing with up to number symbols. number defaults to 2048.
-Map file (new GNU)
Print a load map to file.
-N (GNU, SunOS 4)
Create an OMAGIC format binary. This is the default format for relocatable object files.
OMAGIC format binaries have writable text segments. Where appropriate, this option implies
-Bstatic.
-N (SVR3)
Place the text section at the beginning of the text segment, and the data segment immediately
after the text segment.
-N num (XENIX)
Set the page size to num bytes.
-n (GNU, SunOS 4)
Create an NMAGIC format shared executable binary. The text segment is read-only. Where
appropriate, this option implies -Bstatic.
-n num (XENIX)
Truncate symbol names to num characters.
-noinhibit-exec (new GNU)
Create an output file even if errors are encountered during linking.
-o file (all)
Write output to file instead of the default a.out.
-oformat format (new GNU)
Write the output file in format format.
-P (XENIX)
Disable packing of segments.
-p (SunOS 4)
Start the data segment on a page boundary, even if the text segment is not shared.
-Qyn (Solaris 2)
If yn is y, add an ident string to the .comment section of the output file identifying the version
of the linker used. cc does this by default. -Qn suppresses this header.
-q (old GNU on BSD)
Create a QMAGIC format demand loaded executable binary.
-R file (new GNU)
Read symbol information from file, but do not include it in the output.
-R (XENIX)
Ensure a relocation table of non-zero size.
-Rd offset (XENIX)
Set the data segment relocation offset to offset.
-Rt offset (XENIX)
Set the text segment relocation offset to offset.
-R paths (Solaris 2)
Specify paths as a colon-separated list of directories to be searched for libraries by the run-
time linker.
-r (all)
Generate a relocatable output file.
-S (GNU, SunOS 4)
Strip only stab symbols from a.out files.
-s (all)
Strip all symbols from the output file. This overrides other strip options.
-SE[GMENTS]:number (SCO)
Allow the program to have number segments. The default value is 128.
-sort-common (new GNU)
Disable sorting of common blocks by size.
-ST[ACK]:size (SCO)
Specify that the stack should be size bytes long.
-T file (new GNU)
Read commands from file. These commands override the standard link format. This is the
same as the -c option.
-T address (old GNU, SunOS 4)
Start the text segment at address.
-Tbss address (new GNU)
Start the bss segment at address.
-Tdata address (GNU, SunOS 4)
Start the data segment at address.
-Ttext address (GNU, SunOS 4)
Start the text segment at address. The same as -T.
-t (GNU)
Print the names of input files to stderr as they are processed.
-t (SCO, SVR3, SVR4)
Do not warn about multiply defined symbols of different size.
-u symbol (all)
Consider symbol to be undefined. This can be used to force the extraction of certain files from
a library.
-Ur (new GNU)
Generate relocatable output, like the -r option. For C++ programs only, resolve references to
constructors.
-V (new GNU)
Print full version number information, including supported emulations.
-V (SCO, SVR3, Solaris 2)
Print version number information for ld.
-VS number (SCO, SVR3)
Store version number in the optional header of the output file.
-v (new GNU)
Print version number information for ld only.
CD-ROM producers
A large number of companies produce CD-ROMs, but the following are of particular interest:
InfoMagic, Inc.
P.O. Box 30370
Flagstaff
AZ 86003-0370
Phone: +1-800-800-6613
+1-602-526-9565
431
Fax: +1-602-526-9573
Mail: [email protected]
A number of UNIX-oriented CDs, including Internet tools, Linux, and standards.
1
tcpdump is included in the FreeBSD distribution.
The initials of the CD-ROM publishers are self-evident. The initials in bold print are, in my
personal opinion, the best choice for the package in question. Your mileage may vary.
435
Travels into several remote nations of the world, by Lemuel Gulliver. Jonathan Swift,
Bejamin Motte, London, 1726. A satirical treatment of early 18th century English politics.
Typesetting tables with tbl. Henry McGilton, Mary McNabb, Trilithon Press, 1990. A tutorial
introduction to tbl.
UNIX Curses Explained. Berny Goodheart, Prentice-Hall 1991. A description of BSD and
System V versions of Curses.
UNIX in a Nutshell, for System V and Solaris 2.0. Daniel Gilly and the staff of OReilly &
Associates, Inc. OReilly and Associates Inc., 1992.
UNIX in a Nutshell, for 4.3BSD. c the staff of OReilly & Associates, Inc. OReilly and As-
sociates Inc., 1990.
UNIX Network Programming. W. Richard Stevens, Prentice-Hall 1990. Includes a compari-
son of sockets and STREAMS.
UNIX Power Tools. Jerry Peek, Tim OReilly, Mike Loukides, OReilly and Associates Inc.,
1993. Includes CD-ROM. An indispensible collection of tips and tricks.
UNIX System V Application Binary Interface, Revised Edition, UNIX Press, 1992.
UNIX Time-sharing system UNIX Programmers Manual, Seventh Edition. Holt, Rinehart
and Winston, 1979. The original Seventh Edition UNIX documentation (two volumes).
Understanding and using COFF. Gintaras R. Gircys, OReilly & Associates Inc., 1988. A
description of the Common Object File Format.
Using as. A detailed description of GNU as, Dean Elsner and Jay Fenlason, source only from
the Free Software Foundation. Part of the GNU binutils distribution.
Using uucp and Usenet. Grace Todino and Dale Dougherty, OReilly and Associates Inc.,
1987. The definitive guide to using uucp.
X Window System Administrators Guide (Volume 8 of the X Window System documenta-
tion). Linda Mui, Eric Pearce, OReilly & Associates Inc., 1993. Available with companion
CD-ROM.
lex and yacc, Second Edition. John R. Levine, Tony Mason and Doug Brown, OReilly &
Associates Inc., 1992.
sed and awk. Dale Dougherty, OReilly & Associates Inc., 1992. An in-depth treatment of
both utilities.
sendmail. Bryan Costales with Eric Allman and Neil Rickert, OReilly & Associates Inc.,
1993. Everything you never wanted to know about sendmail.