Initial checkin: 4.4BSD version. These files need to be updated with
current license information and adapted to the FreeBSD build environment before they will build. Approved by: David Taylor <davidt@caldera.com>
This commit is contained in:
parent
b86ebdb239
commit
c11e094d96
Notes:
svn2git
2020-12-20 02:59:44 +00:00
svn path=/head/; revision=96924
11
share/doc/psd/04.uprog/Makefile
Normal file
11
share/doc/psd/04.uprog/Makefile
Normal file
@ -0,0 +1,11 @@
|
||||
# @(#)Makefile 8.1 (Berkeley) 6/8/93
|
||||
# $FreeBSD$
|
||||
|
||||
DIR= psd/04.uprog
|
||||
SRCS= p.mac p0 p1 p2 p3 p4 p5 p6 p8 p9
|
||||
MACROS= -ms
|
||||
|
||||
paper.ps: ${SRCS}
|
||||
${ROFF} ${MAC} ${SRCS} > ${.TARGET}
|
||||
|
||||
.include <bsd.doc.mk>
|
39
share/doc/psd/04.uprog/p.mac
Normal file
39
share/doc/psd/04.uprog/p.mac
Normal file
@ -0,0 +1,39 @@
|
||||
.\" This module is believed to contain source code proprietary to AT&T.
|
||||
.\" Use and redistribution is subject to the Berkeley Software License
|
||||
.\" Agreement and your Software Agreement with AT&T (Western Electric).
|
||||
.\"
|
||||
.\" @(#)p.mac 8.1 (Berkeley) 6/8/93
|
||||
.\"
|
||||
.\" $FreeBSD$
|
||||
.de UC
|
||||
\&\\$3\s-1\\$1\\s0\&\\$2
|
||||
..
|
||||
.de IT
|
||||
\&\\$3\fI\\$1\fR\^\&\\$2
|
||||
..
|
||||
.de UL
|
||||
\%\&\\$3\f(CW\s-1\\$1\s0\fR\&\\$2
|
||||
..
|
||||
.de P1
|
||||
.DS I .5i
|
||||
.nf
|
||||
.ft CW
|
||||
.ps \\n(PS-1
|
||||
.vs \\n(VS-1
|
||||
..
|
||||
.de P2
|
||||
.ps \\n(PS
|
||||
.vs \\n(VS
|
||||
.ft R
|
||||
.DE
|
||||
..
|
||||
.hy 14 \"2=not last lines; 4= no -xx; 8=no xx-
|
||||
.am SH
|
||||
.ft R
|
||||
..
|
||||
.am NH
|
||||
.ft R
|
||||
..
|
||||
.am TL
|
||||
.ft R
|
||||
..
|
49
share/doc/psd/04.uprog/p0
Normal file
49
share/doc/psd/04.uprog/p0
Normal file
@ -0,0 +1,49 @@
|
||||
.\" This module is believed to contain source code proprietary to AT&T.
|
||||
.\" Use and redistribution is subject to the Berkeley Software License
|
||||
.\" Agreement and your Software Agreement with AT&T (Western Electric).
|
||||
.\"
|
||||
.\" @(#)p0 8.1 (Berkeley) 6/8/93
|
||||
.\"
|
||||
.\" $FreeBSD$
|
||||
.if n .ls 1
|
||||
.\" .TM 78-1273-9 39199 39199-11
|
||||
.\" .ND October 2, 1978
|
||||
.\" .old TM 75-1273-11 October 22, 1975
|
||||
.OH 'UNIX Programming \(em Second Edition''PSD:4-%'
|
||||
.EH 'PSD:4-%''UNIX Programming \(em Second Edition'
|
||||
.TL
|
||||
UNIX Programming \(em Second Edition
|
||||
.AU "MH 2C-518" 6021
|
||||
Brian W. Kernighan
|
||||
.AU "MH 2C-517" 3770
|
||||
Dennis M. Ritchie
|
||||
.AI
|
||||
AT&T Bell Laboratories
|
||||
Murray Hill, NJ 07974
|
||||
.AB
|
||||
.PP
|
||||
This paper is an introduction to programming on
|
||||
the
|
||||
.UX
|
||||
system.
|
||||
The emphasis is on how to write programs that interface
|
||||
to the operating system,
|
||||
either directly or through the standard I/O library.
|
||||
The topics discussed include
|
||||
.IP " \(bu"
|
||||
handling command arguments
|
||||
.IP " \(bu"
|
||||
rudimentary I/O; the standard input and output
|
||||
.IP " \(bu"
|
||||
the standard I/O library; file system access
|
||||
.IP " \(bu"
|
||||
low-level I/O: open, read, write, close, seek
|
||||
.IP " \(bu"
|
||||
processes: exec, fork, pipes
|
||||
.IP " \(bu"
|
||||
signals \(em interrupts, etc.
|
||||
.PP
|
||||
There is also an appendix which describes
|
||||
the standard I/O library in detail.
|
||||
.AE
|
||||
.\" .CS 17 0 17 0 0 4
|
55
share/doc/psd/04.uprog/p1
Normal file
55
share/doc/psd/04.uprog/p1
Normal file
@ -0,0 +1,55 @@
|
||||
.\" This module is believed to contain source code proprietary to AT&T.
|
||||
.\" Use and redistribution is subject to the Berkeley Software License
|
||||
.\" Agreement and your Software Agreement with AT&T (Western Electric).
|
||||
.\"
|
||||
.\" @(#)p1 8.1 (Berkeley) 6/8/93
|
||||
.\"
|
||||
.\" $FreeBSD$
|
||||
.if n .ls 2
|
||||
.if t .tr |\(or
|
||||
.NH
|
||||
INTRODUCTION
|
||||
.PP
|
||||
This paper describes how to write
|
||||
programs
|
||||
that interface with the
|
||||
.UC UNIX
|
||||
operating system in a non-trivial way.
|
||||
This includes programs that use files by name,
|
||||
that use pipes,
|
||||
that invoke other commands as they run,
|
||||
or that attempt to catch interrupts and other signals
|
||||
during execution.
|
||||
.PP
|
||||
The document collects material which is scattered
|
||||
throughout several sections of
|
||||
.I
|
||||
The
|
||||
.UC UNIX
|
||||
Programmer's Manual
|
||||
.R
|
||||
[1]
|
||||
for Version 7
|
||||
.UC UNIX .
|
||||
There is no attempt to be complete;
|
||||
only generally useful material is dealt with.
|
||||
It is assumed that you will be programming in C,
|
||||
so you must be able to read the language
|
||||
roughly up to the level of
|
||||
.I
|
||||
The C Programming Language
|
||||
.R
|
||||
[2].
|
||||
Some of the material in sections 2 through 4
|
||||
is based on
|
||||
topics covered more carefully there.
|
||||
You should also be familiar with
|
||||
.UC UNIX
|
||||
itself
|
||||
at least
|
||||
to the level of
|
||||
.I
|
||||
.UC UNIX
|
||||
for Beginners
|
||||
.R
|
||||
[3].
|
242
share/doc/psd/04.uprog/p2
Normal file
242
share/doc/psd/04.uprog/p2
Normal file
@ -0,0 +1,242 @@
|
||||
.\" This module is believed to contain source code proprietary to AT&T.
|
||||
.\" Use and redistribution is subject to the Berkeley Software License
|
||||
.\" Agreement and your Software Agreement with AT&T (Western Electric).
|
||||
.\"
|
||||
.\" @(#)p2 8.1 (Berkeley) 6/8/93
|
||||
.\"
|
||||
.\" $FreeBSD$
|
||||
.NH
|
||||
BASICS
|
||||
.NH 2
|
||||
Program Arguments
|
||||
.PP
|
||||
When a C program is run as a command,
|
||||
the arguments on the command line are made available
|
||||
to the
|
||||
function
|
||||
.UL main
|
||||
as an argument count
|
||||
.UL argc
|
||||
and an array
|
||||
.UL argv
|
||||
of
|
||||
pointers to
|
||||
character strings
|
||||
that contain
|
||||
the arguments.
|
||||
By convention,
|
||||
.UL argv[0]
|
||||
is the command name itself,
|
||||
so
|
||||
.UL argc
|
||||
is always greater than 0.
|
||||
.PP
|
||||
The following program illustrates the mechanism:
|
||||
it simply echoes its arguments
|
||||
back to the terminal.
|
||||
(This is essentially the
|
||||
.UL echo
|
||||
command.)
|
||||
.P1
|
||||
main(argc, argv) /* echo arguments */
|
||||
int argc;
|
||||
char *argv[];
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 1; i < argc; i++)
|
||||
printf("%s%c", argv[i], (i<argc-1) ? ' ' : '\en');
|
||||
}
|
||||
.P2
|
||||
.UL argv
|
||||
is a pointer to an array
|
||||
whose individual elements are pointers to arrays of characters;
|
||||
each is terminated by
|
||||
.UL \e0 ,
|
||||
so they can be treated as strings.
|
||||
The program starts by printing
|
||||
.UL argv[1]
|
||||
and loops until it has printed them all.
|
||||
.PP
|
||||
The argument count and the arguments
|
||||
are parameters to
|
||||
.UL main .
|
||||
If you want to keep them around so other
|
||||
routines can get at them, you must
|
||||
copy them to external variables.
|
||||
.NH 2
|
||||
The ``Standard Input'' and ``Standard Output''
|
||||
.PP
|
||||
The simplest input mechanism is to read the ``standard input,''
|
||||
which is generally the user's terminal.
|
||||
The function
|
||||
.UL getchar
|
||||
returns the next input character each time it is called.
|
||||
A file may be substituted for the terminal by
|
||||
using the
|
||||
.UL <
|
||||
convention:
|
||||
if
|
||||
.UL prog
|
||||
uses
|
||||
.UL getchar ,
|
||||
then
|
||||
the command line
|
||||
.P1
|
||||
prog <file
|
||||
.P2
|
||||
causes
|
||||
.UL prog
|
||||
to read
|
||||
.UL file
|
||||
instead of the terminal.
|
||||
.UL prog
|
||||
itself need know nothing about where its input
|
||||
is coming from.
|
||||
This is also true if the input comes from another program via
|
||||
the
|
||||
.U
|
||||
pipe mechanism:
|
||||
.P1
|
||||
otherprog | prog
|
||||
.P2
|
||||
provides the standard input for
|
||||
.UL prog
|
||||
from the standard output of
|
||||
.UL otherprog.
|
||||
.PP
|
||||
.UL getchar
|
||||
returns the value
|
||||
.UL EOF
|
||||
when it encounters the end of file
|
||||
(or an error)
|
||||
on whatever you are reading.
|
||||
The value of
|
||||
.UL EOF
|
||||
is normally defined to be
|
||||
.UL -1 ,
|
||||
but it is unwise to take any advantage
|
||||
of that knowledge.
|
||||
As will become clear shortly,
|
||||
this value is automatically defined for you when
|
||||
you compile a program,
|
||||
and need not be of any concern.
|
||||
.PP
|
||||
Similarly,
|
||||
.UL putchar(c)
|
||||
puts the character
|
||||
.UL c
|
||||
on the ``standard output,''
|
||||
which is also by default the terminal.
|
||||
The output can be captured on a file
|
||||
by using
|
||||
.UL > :
|
||||
if
|
||||
.UL prog
|
||||
uses
|
||||
.UL putchar ,
|
||||
.P1
|
||||
prog >outfile
|
||||
.P2
|
||||
writes the standard output on
|
||||
.UL outfile
|
||||
instead of the terminal.
|
||||
.UL outfile
|
||||
is created if it doesn't exist;
|
||||
if it already exists, its previous contents are overwritten.
|
||||
And a pipe can be used:
|
||||
.P1
|
||||
prog | otherprog
|
||||
.P2
|
||||
puts the standard output of
|
||||
.UL prog
|
||||
into the standard input of
|
||||
.UL otherprog.
|
||||
.PP
|
||||
The function
|
||||
.UL printf ,
|
||||
which formats output in various ways,
|
||||
uses
|
||||
the same mechanism as
|
||||
.UL putchar
|
||||
does,
|
||||
so calls to
|
||||
.UL printf
|
||||
and
|
||||
.UL putchar
|
||||
may be intermixed in any order;
|
||||
the output will appear in the order of the calls.
|
||||
.PP
|
||||
Similarly, the function
|
||||
.UL scanf
|
||||
provides for formatted input conversion;
|
||||
it will read the standard input and break it
|
||||
up into strings, numbers, etc.,
|
||||
as desired.
|
||||
.UL scanf
|
||||
uses the same mechanism as
|
||||
.UL getchar ,
|
||||
so calls to them may also be intermixed.
|
||||
.PP
|
||||
Many programs
|
||||
read only one input and write one output;
|
||||
for such programs I/O
|
||||
with
|
||||
.UL getchar ,
|
||||
.UL putchar ,
|
||||
.UL scanf ,
|
||||
and
|
||||
.UL printf
|
||||
may be entirely adequate,
|
||||
and it is almost always enough to get started.
|
||||
This is particularly true if
|
||||
the
|
||||
.UC UNIX
|
||||
pipe facility is used to connect the output of
|
||||
one program to the input of the next.
|
||||
For example, the following program
|
||||
strips out all ascii control characters
|
||||
from its input
|
||||
(except for newline and tab).
|
||||
.P1
|
||||
#include <stdio.h>
|
||||
|
||||
main() /* ccstrip: strip non-graphic characters */
|
||||
{
|
||||
int c;
|
||||
while ((c = getchar()) != EOF)
|
||||
if ((c >= ' ' && c < 0177) || c == '\et' || c == '\en')
|
||||
putchar(c);
|
||||
exit(0);
|
||||
}
|
||||
.P2
|
||||
The line
|
||||
.P1
|
||||
#include <stdio.h>
|
||||
.P2
|
||||
should appear at the beginning of each source file.
|
||||
It causes the C compiler to read a file
|
||||
.IT /usr/include/stdio.h ) (
|
||||
of
|
||||
standard routines and symbols
|
||||
that includes the definition of
|
||||
.UL EOF .
|
||||
.PP
|
||||
If it is necessary to treat multiple files,
|
||||
you can use
|
||||
.UL cat
|
||||
to collect the files for you:
|
||||
.P1
|
||||
cat file1 file2 ... | ccstrip >output
|
||||
.P2
|
||||
and thus avoid learning how to access files from a program.
|
||||
By the way,
|
||||
the call to
|
||||
.UL exit
|
||||
at the end is not necessary to make the program work
|
||||
properly,
|
||||
but it assures that any caller
|
||||
of the program will see a normal termination status
|
||||
(conventionally 0)
|
||||
from the program when it completes.
|
||||
Section 6 discusses status returns in more detail.
|
436
share/doc/psd/04.uprog/p3
Normal file
436
share/doc/psd/04.uprog/p3
Normal file
@ -0,0 +1,436 @@
|
||||
.\" This module is believed to contain source code proprietary to AT&T.
|
||||
.\" Use and redistribution is subject to the Berkeley Software License
|
||||
.\" Agreement and your Software Agreement with AT&T (Western Electric).
|
||||
.\"
|
||||
.\" @(#)p3 8.1 (Berkeley) 6/8/93
|
||||
.\"
|
||||
.\" $FreeBSD$
|
||||
.NH
|
||||
THE STANDARD I/O LIBRARY
|
||||
.PP
|
||||
The ``Standard I/O Library''
|
||||
is a collection of routines
|
||||
intended to provide
|
||||
efficient
|
||||
and portable
|
||||
I/O services
|
||||
for most C programs.
|
||||
The standard I/O library is available on each system that supports C,
|
||||
so programs that confine
|
||||
their system interactions
|
||||
to its facilities
|
||||
can be transported from one system to another essentially without change.
|
||||
.PP
|
||||
In this section, we will discuss the basics of the standard I/O library.
|
||||
The appendix contains a more complete description of its capabilities.
|
||||
.NH 2
|
||||
File Access
|
||||
.PP
|
||||
The programs written so far have all
|
||||
read the standard input and written the standard output,
|
||||
which we have assumed are magically pre-defined.
|
||||
The next step
|
||||
is to write a program that accesses
|
||||
a file that is
|
||||
.ul
|
||||
not
|
||||
already connected to the program.
|
||||
One simple example is
|
||||
.IT wc ,
|
||||
which counts the lines, words and characters
|
||||
in a set of files.
|
||||
For instance, the command
|
||||
.P1
|
||||
wc x.c y.c
|
||||
.P2
|
||||
prints the number of lines, words and characters
|
||||
in
|
||||
.UL x.c
|
||||
and
|
||||
.UL y.c
|
||||
and the totals.
|
||||
.PP
|
||||
The question is how to arrange for the named files
|
||||
to be read \(em
|
||||
that is, how to connect the file system names
|
||||
to the I/O statements which actually read the data.
|
||||
.PP
|
||||
The rules are simple.
|
||||
Before it can be read or written
|
||||
a file has to be
|
||||
.ul
|
||||
opened
|
||||
by the standard library function
|
||||
.UL fopen .
|
||||
.UL fopen
|
||||
takes an external name
|
||||
(like
|
||||
.UL x.c
|
||||
or
|
||||
.UL y.c ),
|
||||
does some housekeeping and negotiation with the operating system,
|
||||
and returns an internal name
|
||||
which must be used in subsequent
|
||||
reads or writes of the file.
|
||||
.PP
|
||||
This internal name is actually a pointer,
|
||||
called a
|
||||
.IT file
|
||||
.IT pointer ,
|
||||
to a structure
|
||||
which contains information about the file,
|
||||
such as the location of a buffer,
|
||||
the current character position in the buffer,
|
||||
whether the file is being read or written,
|
||||
and the like.
|
||||
Users don't need to know the details,
|
||||
because part of the standard I/O definitions
|
||||
obtained by including
|
||||
.UL stdio.h
|
||||
is a structure definition called
|
||||
.UL FILE .
|
||||
The only declaration needed for a file pointer
|
||||
is exemplified by
|
||||
.P1
|
||||
FILE *fp, *fopen();
|
||||
.P2
|
||||
This says that
|
||||
.UL fp
|
||||
is a pointer to a
|
||||
.UL FILE ,
|
||||
and
|
||||
.UL fopen
|
||||
returns a pointer to
|
||||
a
|
||||
.UL FILE .
|
||||
.UL FILE \& (
|
||||
is a type name, like
|
||||
.UL int ,
|
||||
not a structure tag.
|
||||
.PP
|
||||
The actual call to
|
||||
.UL fopen
|
||||
in a program
|
||||
is
|
||||
.P1
|
||||
fp = fopen(name, mode);
|
||||
.P2
|
||||
The first argument of
|
||||
.UL fopen
|
||||
is the
|
||||
name
|
||||
of the file,
|
||||
as a character string.
|
||||
The second argument is the
|
||||
mode,
|
||||
also as a character string,
|
||||
which indicates how you intend to
|
||||
use the file.
|
||||
The only allowable modes are
|
||||
read
|
||||
.UL \&"r" ), (
|
||||
write
|
||||
.UL \&"w" ), (
|
||||
or append
|
||||
.UL \&"a" ). (
|
||||
.PP
|
||||
If a file that you open for writing or appending does not exist,
|
||||
it is created
|
||||
(if possible).
|
||||
Opening an existing file for writing causes the old contents
|
||||
to be discarded.
|
||||
Trying to read a file that does not exist
|
||||
is an error,
|
||||
and there may be other causes of error
|
||||
as well
|
||||
(like trying to read a file
|
||||
when you don't have permission).
|
||||
If there is any error,
|
||||
.UL fopen
|
||||
will return the null pointer
|
||||
value
|
||||
.UL NULL
|
||||
(which is defined as zero in
|
||||
.UL stdio.h ).
|
||||
.PP
|
||||
The next thing needed is a way to read or write the file
|
||||
once it is open.
|
||||
There are several possibilities,
|
||||
of which
|
||||
.UL getc
|
||||
and
|
||||
.UL putc
|
||||
are the simplest.
|
||||
.UL getc
|
||||
returns the next character from a file;
|
||||
it needs the file pointer to tell it what file.
|
||||
Thus
|
||||
.P1
|
||||
c = getc(fp)
|
||||
.P2
|
||||
places in
|
||||
.UL c
|
||||
the next character from the file referred to by
|
||||
.UL fp ;
|
||||
it returns
|
||||
.UL EOF
|
||||
when it reaches end of file.
|
||||
.UL putc
|
||||
is the inverse of
|
||||
.UL getc :
|
||||
.P1
|
||||
putc(c, fp)
|
||||
.P2
|
||||
puts the character
|
||||
.UL c
|
||||
on the file
|
||||
.UL fp
|
||||
and returns
|
||||
.UL c .
|
||||
.UL getc
|
||||
and
|
||||
.UL putc
|
||||
return
|
||||
.UL EOF
|
||||
on error.
|
||||
.PP
|
||||
When a program is started, three files are opened automatically,
|
||||
and file pointers are provided for them.
|
||||
These files are the standard input,
|
||||
the standard output,
|
||||
and the standard error output;
|
||||
the corresponding file pointers are
|
||||
called
|
||||
.UL stdin ,
|
||||
.UL stdout ,
|
||||
and
|
||||
.UL stderr .
|
||||
Normally these are all connected to the terminal,
|
||||
but
|
||||
may be redirected to files or pipes as described in
|
||||
Section 2.2.
|
||||
.UL stdin ,
|
||||
.UL stdout
|
||||
and
|
||||
.UL stderr
|
||||
are pre-defined in the I/O library
|
||||
as the standard input, output and error files;
|
||||
they may be used anywhere an object of type
|
||||
.UL FILE\ *
|
||||
can be.
|
||||
They are
|
||||
constants, however,
|
||||
.ul
|
||||
not
|
||||
variables,
|
||||
so don't try to assign to them.
|
||||
.PP
|
||||
With some of the preliminaries out of the way,
|
||||
we can now write
|
||||
.IT wc .
|
||||
The basic design
|
||||
is one that has been found
|
||||
convenient for many programs:
|
||||
if there are command-line arguments, they are processed in order.
|
||||
If there are no arguments, the standard input
|
||||
is processed.
|
||||
This way the program can be used stand-alone
|
||||
or as part of a larger process.
|
||||
.P1
|
||||
#include <stdio.h>
|
||||
|
||||
main(argc, argv) /* wc: count lines, words, chars */
|
||||
int argc;
|
||||
char *argv[];
|
||||
{
|
||||
int c, i, inword;
|
||||
FILE *fp, *fopen();
|
||||
long linect, wordct, charct;
|
||||
long tlinect = 0, twordct = 0, tcharct = 0;
|
||||
|
||||
i = 1;
|
||||
fp = stdin;
|
||||
do {
|
||||
if (argc > 1 && (fp=fopen(argv[i], "r")) == NULL) {
|
||||
fprintf(stderr, "wc: can't open %s\en", argv[i]);
|
||||
continue;
|
||||
}
|
||||
linect = wordct = charct = inword = 0;
|
||||
while ((c = getc(fp)) != EOF) {
|
||||
charct++;
|
||||
if (c == '\en')
|
||||
linect++;
|
||||
if (c == ' ' || c == '\et' || c == '\en')
|
||||
inword = 0;
|
||||
else if (inword == 0) {
|
||||
inword = 1;
|
||||
wordct++;
|
||||
}
|
||||
}
|
||||
printf("%7ld %7ld %7ld", linect, wordct, charct);
|
||||
printf(argc > 1 ? " %s\en" : "\en", argv[i]);
|
||||
fclose(fp);
|
||||
tlinect += linect;
|
||||
twordct += wordct;
|
||||
tcharct += charct;
|
||||
} while (++i < argc);
|
||||
if (argc > 2)
|
||||
printf("%7ld %7ld %7ld total\en", tlinect, twordct, tcharct);
|
||||
exit(0);
|
||||
}
|
||||
.P2
|
||||
The function
|
||||
.UL fprintf
|
||||
is identical to
|
||||
.UL printf ,
|
||||
save that the first argument is a file pointer
|
||||
that specifies the file to be
|
||||
written.
|
||||
.PP
|
||||
The function
|
||||
.UL fclose
|
||||
is the inverse of
|
||||
.UL fopen ;
|
||||
it breaks the connection between the file pointer and the external name
|
||||
that was established by
|
||||
.UL fopen ,
|
||||
freeing the
|
||||
file pointer for another file.
|
||||
Since there is a limit on the number
|
||||
of files
|
||||
that a program may have open simultaneously,
|
||||
it's a good idea to free things when they are no longer needed.
|
||||
There is also another reason to call
|
||||
.UL fclose
|
||||
on an output file
|
||||
\(em it flushes the buffer
|
||||
in which
|
||||
.UL putc
|
||||
is collecting output.
|
||||
.UL fclose \& (
|
||||
is called automatically for each open file
|
||||
when a program terminates normally.)
|
||||
.NH 2
|
||||
Error Handling \(em Stderr and Exit
|
||||
.PP
|
||||
.UL stderr
|
||||
is assigned to a program in the same way that
|
||||
.UL stdin
|
||||
and
|
||||
.UL stdout
|
||||
are.
|
||||
Output written on
|
||||
.UL stderr
|
||||
appears on the user's terminal
|
||||
even if the standard output is redirected.
|
||||
.IT wc
|
||||
writes its diagnostics on
|
||||
.UL stderr
|
||||
instead of
|
||||
.UL stdout
|
||||
so that if one of the files can't
|
||||
be accessed for some reason,
|
||||
the message
|
||||
finds its way to the user's terminal instead of disappearing
|
||||
down a pipeline
|
||||
or into an output file.
|
||||
.PP
|
||||
The program actually signals errors in another way,
|
||||
using the function
|
||||
.UL exit
|
||||
to terminate program execution.
|
||||
The argument of
|
||||
.UL exit
|
||||
is available to whatever process
|
||||
called it (see Section 6),
|
||||
so the success or failure
|
||||
of the program can be tested by another program
|
||||
that uses this one as a sub-process.
|
||||
By convention, a return value of 0
|
||||
signals that all is well;
|
||||
non-zero values signal abnormal situations.
|
||||
.PP
|
||||
.UL exit
|
||||
itself
|
||||
calls
|
||||
.UL fclose
|
||||
for each open output file,
|
||||
to flush out any buffered output,
|
||||
then calls
|
||||
a routine named
|
||||
.UL _exit .
|
||||
The function
|
||||
.UL _exit
|
||||
causes immediate termination without any buffer flushing;
|
||||
it may be called directly if desired.
|
||||
.NH 2
|
||||
Miscellaneous I/O Functions
|
||||
.PP
|
||||
The standard I/O library provides several other I/O functions
|
||||
besides those we have illustrated above.
|
||||
.PP
|
||||
Normally output with
|
||||
.UL putc ,
|
||||
etc., is buffered (except to
|
||||
.UL stderr );
|
||||
to force it out immediately, use
|
||||
.UL fflush(fp) .
|
||||
.PP
|
||||
.UL fscanf
|
||||
is identical to
|
||||
.UL scanf ,
|
||||
except that its first argument is a file pointer
|
||||
(as with
|
||||
.UL fprintf )
|
||||
that specifies the file from which the input comes;
|
||||
it returns
|
||||
.UL EOF
|
||||
at end of file.
|
||||
.PP
|
||||
The functions
|
||||
.UL sscanf
|
||||
and
|
||||
.UL sprintf
|
||||
are identical to
|
||||
.UL fscanf
|
||||
and
|
||||
.UL fprintf ,
|
||||
except that the first argument names a character string
|
||||
instead of a file pointer.
|
||||
The conversion is done from the string
|
||||
for
|
||||
.UL sscanf
|
||||
and into it for
|
||||
.UL sprintf .
|
||||
.PP
|
||||
.UL fgets(buf,\ size,\ fp)
|
||||
copies the next line from
|
||||
.UL fp ,
|
||||
up to and including a newline,
|
||||
into
|
||||
.UL buf ;
|
||||
at most
|
||||
.UL size-1
|
||||
characters are copied;
|
||||
it returns
|
||||
.UL NULL
|
||||
at end of file.
|
||||
.UL fputs(buf,\ fp)
|
||||
writes the string in
|
||||
.UL buf
|
||||
onto file
|
||||
.UL fp .
|
||||
.PP
|
||||
The function
|
||||
.UL ungetc(c,\ fp)
|
||||
``pushes back'' the character
|
||||
.UL c
|
||||
onto the input stream
|
||||
.UL fp ;
|
||||
a subsequent call to
|
||||
.UL getc ,
|
||||
.UL fscanf ,
|
||||
etc.,
|
||||
will encounter
|
||||
.UL c .
|
||||
Only one character of pushback per file is permitted.
|
567
share/doc/psd/04.uprog/p4
Normal file
567
share/doc/psd/04.uprog/p4
Normal file
@ -0,0 +1,567 @@
|
||||
.\" This module is believed to contain source code proprietary to AT&T.
|
||||
.\" Use and redistribution is subject to the Berkeley Software License
|
||||
.\" Agreement and your Software Agreement with AT&T (Western Electric).
|
||||
.\"
|
||||
.\" @(#)p4 8.1 (Berkeley) 6/8/93
|
||||
.\"
|
||||
.\" $FreeBSD$
|
||||
.NH
|
||||
LOW-LEVEL I/O
|
||||
.PP
|
||||
This section describes the
|
||||
bottom level of I/O on the
|
||||
.UC UNIX
|
||||
system.
|
||||
The lowest level of I/O in
|
||||
.UC UNIX
|
||||
provides no buffering or any other services;
|
||||
it is in fact a direct entry into the operating system.
|
||||
You are entirely on your own,
|
||||
but on the other hand,
|
||||
you have the most control over what happens.
|
||||
And since the calls and usage are quite simple,
|
||||
this isn't as bad as it sounds.
|
||||
.NH 2
|
||||
File Descriptors
|
||||
.PP
|
||||
In the
|
||||
.UC UNIX
|
||||
operating system,
|
||||
all input and output is done
|
||||
by reading or writing files,
|
||||
because all peripheral devices, even the user's terminal,
|
||||
are files in the file system.
|
||||
This means that a single, homogeneous interface
|
||||
handles all communication between a program and peripheral devices.
|
||||
.PP
|
||||
In the most general case,
|
||||
before reading or writing a file,
|
||||
it is necessary to inform the system
|
||||
of your intent to do so,
|
||||
a process called
|
||||
``opening'' the file.
|
||||
If you are going to write on a file,
|
||||
it may also be necessary to create it.
|
||||
The system checks your right to do so
|
||||
(Does the file exist?
|
||||
Do you have permission to access it?),
|
||||
and if all is well,
|
||||
returns a small positive integer
|
||||
called a
|
||||
.ul
|
||||
file descriptor.
|
||||
Whenever I/O is to be done on the file,
|
||||
the file descriptor is used instead of the name to identify the file.
|
||||
(This is roughly analogous to the use of
|
||||
.UC READ(5,...)
|
||||
and
|
||||
.UC WRITE(6,...)
|
||||
in Fortran.)
|
||||
All
|
||||
information about an open file is maintained by the system;
|
||||
the user program refers to the file
|
||||
only
|
||||
by the file descriptor.
|
||||
.PP
|
||||
The file pointers discussed in section 3
|
||||
are similar in spirit to file descriptors,
|
||||
but file descriptors are more fundamental.
|
||||
A file pointer is a pointer to a structure that contains,
|
||||
among other things, the file descriptor for the file in question.
|
||||
.PP
|
||||
Since input and output involving the user's terminal
|
||||
are so common,
|
||||
special arrangements exist to make this convenient.
|
||||
When the command interpreter (the
|
||||
``shell'')
|
||||
runs a program,
|
||||
it opens
|
||||
three files, with file descriptors 0, 1, and 2,
|
||||
called the standard input,
|
||||
the standard output, and the standard error output.
|
||||
All of these are normally connected to the terminal,
|
||||
so if a program reads file descriptor 0
|
||||
and writes file descriptors 1 and 2,
|
||||
it can do terminal I/O
|
||||
without worrying about opening the files.
|
||||
.PP
|
||||
If I/O is redirected
|
||||
to and from files with
|
||||
.UL <
|
||||
and
|
||||
.UL > ,
|
||||
as in
|
||||
.P1
|
||||
prog <infile >outfile
|
||||
.P2
|
||||
the shell changes the default assignments for file descriptors
|
||||
0 and 1
|
||||
from the terminal to the named files.
|
||||
Similar observations hold if the input or output is associated with a pipe.
|
||||
Normally file descriptor 2 remains attached to the terminal,
|
||||
so error messages can go there.
|
||||
In all cases,
|
||||
the file assignments are changed by the shell,
|
||||
not by the program.
|
||||
The program does not need to know where its input
|
||||
comes from nor where its output goes,
|
||||
so long as it uses file 0 for input and 1 and 2 for output.
|
||||
.NH 2
|
||||
Read and Write
|
||||
.PP
|
||||
All input and output is done by
|
||||
two functions called
|
||||
.UL read
|
||||
and
|
||||
.UL write .
|
||||
For both, the first argument is a file descriptor.
|
||||
The second argument is a buffer in your program where the data is to
|
||||
come from or go to.
|
||||
The third argument is the number of bytes to be transferred.
|
||||
The calls are
|
||||
.P1
|
||||
n_read = read(fd, buf, n);
|
||||
|
||||
n_written = write(fd, buf, n);
|
||||
.P2
|
||||
Each call returns a byte count
|
||||
which is the number of bytes actually transferred.
|
||||
On reading,
|
||||
the number of bytes returned may be less than
|
||||
the number asked for,
|
||||
because fewer than
|
||||
.UL n
|
||||
bytes remained to be read.
|
||||
(When the file is a terminal,
|
||||
.UL read
|
||||
normally reads only up to the next newline,
|
||||
which is generally less than what was requested.)
|
||||
A return value of zero bytes implies end of file,
|
||||
and
|
||||
.UL -1
|
||||
indicates an error of some sort.
|
||||
For writing, the returned value is the number of bytes
|
||||
actually written;
|
||||
it is generally an error if this isn't equal
|
||||
to the number supposed to be written.
|
||||
.PP
|
||||
The number of bytes to be read or written is quite arbitrary.
|
||||
The two most common values are
|
||||
1,
|
||||
which means one character at a time
|
||||
(``unbuffered''),
|
||||
and
|
||||
512,
|
||||
which corresponds to a physical blocksize on many peripheral devices.
|
||||
This latter size will be most efficient,
|
||||
but even character at a time I/O
|
||||
is not inordinately expensive.
|
||||
.PP
|
||||
Putting these facts together,
|
||||
we can write a simple program to copy
|
||||
its input to its output.
|
||||
This program will copy anything to anything,
|
||||
since the input and output can be redirected to any file or device.
|
||||
.P1
|
||||
#define BUFSIZE 512 /* best size for PDP-11 UNIX */
|
||||
|
||||
main() /* copy input to output */
|
||||
{
|
||||
char buf[BUFSIZE];
|
||||
int n;
|
||||
|
||||
while ((n = read(0, buf, BUFSIZE)) > 0)
|
||||
write(1, buf, n);
|
||||
exit(0);
|
||||
}
|
||||
.P2
|
||||
If the file size is not a multiple of
|
||||
.UL BUFSIZE ,
|
||||
some
|
||||
.UL read
|
||||
will return a smaller number of bytes
|
||||
to be written by
|
||||
.UL write ;
|
||||
the next call to
|
||||
.UL read
|
||||
after that
|
||||
will return zero.
|
||||
.PP
|
||||
It is instructive to see how
|
||||
.UL read
|
||||
and
|
||||
.UL write
|
||||
can be used to construct
|
||||
higher level routines like
|
||||
.UL getchar ,
|
||||
.UL putchar ,
|
||||
etc.
|
||||
For example,
|
||||
here is a version of
|
||||
.UL getchar
|
||||
which does unbuffered input.
|
||||
.P1
|
||||
#define CMASK 0377 /* for making char's > 0 */
|
||||
|
||||
getchar() /* unbuffered single character input */
|
||||
{
|
||||
char c;
|
||||
|
||||
return((read(0, &c, 1) > 0) ? c & CMASK : EOF);
|
||||
}
|
||||
.P2
|
||||
.UL c
|
||||
.ul
|
||||
must
|
||||
be declared
|
||||
.UL char ,
|
||||
because
|
||||
.UL read
|
||||
accepts a character pointer.
|
||||
The character being returned must be masked with
|
||||
.UL 0377
|
||||
to ensure that it is positive;
|
||||
otherwise sign extension may make it negative.
|
||||
(The constant
|
||||
.UL 0377
|
||||
is appropriate for the
|
||||
.UC PDP -11
|
||||
but not necessarily for other machines.)
|
||||
.PP
|
||||
The second version of
|
||||
.UL getchar
|
||||
does input in big chunks,
|
||||
and hands out the characters one at a time.
|
||||
.P1
|
||||
#define CMASK 0377 /* for making char's > 0 */
|
||||
#define BUFSIZE 512
|
||||
|
||||
getchar() /* buffered version */
|
||||
{
|
||||
static char buf[BUFSIZE];
|
||||
static char *bufp = buf;
|
||||
static int n = 0;
|
||||
|
||||
if (n == 0) { /* buffer is empty */
|
||||
n = read(0, buf, BUFSIZE);
|
||||
bufp = buf;
|
||||
}
|
||||
return((--n >= 0) ? *bufp++ & CMASK : EOF);
|
||||
}
|
||||
.P2
|
||||
.NH 2
|
||||
Open, Creat, Close, Unlink
|
||||
.PP
|
||||
Other than the default
|
||||
standard input, output and error files,
|
||||
you must explicitly open files in order to
|
||||
read or write them.
|
||||
There are two system entry points for this,
|
||||
.UL open
|
||||
and
|
||||
.UL creat
|
||||
[sic].
|
||||
.PP
|
||||
.UL open
|
||||
is rather like the
|
||||
.UL fopen
|
||||
discussed in the previous section,
|
||||
except that instead of returning a file pointer,
|
||||
it returns a file descriptor,
|
||||
which is just an
|
||||
.UL int .
|
||||
.P1
|
||||
int fd;
|
||||
|
||||
fd = open(name, rwmode);
|
||||
.P2
|
||||
As with
|
||||
.UL fopen ,
|
||||
the
|
||||
.UL name
|
||||
argument
|
||||
is a character string corresponding to the external file name.
|
||||
The access mode argument
|
||||
is different, however:
|
||||
.UL rwmode
|
||||
is 0 for read, 1 for write, and 2 for read and write access.
|
||||
.UL open
|
||||
returns
|
||||
.UL -1
|
||||
if any error occurs;
|
||||
otherwise it returns a valid file descriptor.
|
||||
.PP
|
||||
It is an error to
|
||||
try to
|
||||
.UL open
|
||||
a file that does not exist.
|
||||
The entry point
|
||||
.UL creat
|
||||
is provided to create new files,
|
||||
or to re-write old ones.
|
||||
.P1
|
||||
fd = creat(name, pmode);
|
||||
.P2
|
||||
returns a file descriptor
|
||||
if it was able to create the file
|
||||
called
|
||||
.UL name ,
|
||||
and
|
||||
.UL -1
|
||||
if not.
|
||||
If the file
|
||||
already exists,
|
||||
.UL creat
|
||||
will truncate it to zero length;
|
||||
it is not an error to
|
||||
.UL creat
|
||||
a file that already exists.
|
||||
.PP
|
||||
If the file is brand new,
|
||||
.UL creat
|
||||
creates it with the
|
||||
.ul
|
||||
protection mode
|
||||
specified by
|
||||
the
|
||||
.UL pmode
|
||||
argument.
|
||||
In the
|
||||
.UC UNIX
|
||||
file system,
|
||||
there are nine bits of protection information
|
||||
associated with a file,
|
||||
controlling read, write and execute permission for
|
||||
the owner of the file,
|
||||
for the owner's group,
|
||||
and for all others.
|
||||
Thus a three-digit octal number
|
||||
is most convenient for specifying the permissions.
|
||||
For example,
|
||||
0755
|
||||
specifies read, write and execute permission for the owner,
|
||||
and read and execute permission for the group and everyone else.
|
||||
.PP
|
||||
To illustrate,
|
||||
here is a simplified version of
|
||||
the
|
||||
.UC UNIX
|
||||
utility
|
||||
.IT cp ,
|
||||
a program which copies one file to another.
|
||||
(The main simplification is that our version
|
||||
copies only one file,
|
||||
and does not permit the second argument
|
||||
to be a directory.)
|
||||
.P1
|
||||
#define NULL 0
|
||||
#define BUFSIZE 512
|
||||
#define PMODE 0644 /* RW for owner, R for group, others */
|
||||
|
||||
main(argc, argv) /* cp: copy f1 to f2 */
|
||||
int argc;
|
||||
char *argv[];
|
||||
{
|
||||
int f1, f2, n;
|
||||
char buf[BUFSIZE];
|
||||
|
||||
if (argc != 3)
|
||||
error("Usage: cp from to", NULL);
|
||||
if ((f1 = open(argv[1], 0)) == -1)
|
||||
error("cp: can't open %s", argv[1]);
|
||||
if ((f2 = creat(argv[2], PMODE)) == -1)
|
||||
error("cp: can't create %s", argv[2]);
|
||||
|
||||
while ((n = read(f1, buf, BUFSIZE)) > 0)
|
||||
if (write(f2, buf, n) != n)
|
||||
error("cp: write error", NULL);
|
||||
exit(0);
|
||||
}
|
||||
.P2
|
||||
.P1
|
||||
error(s1, s2) /* print error message and die */
|
||||
char *s1, *s2;
|
||||
{
|
||||
printf(s1, s2);
|
||||
printf("\en");
|
||||
exit(1);
|
||||
}
|
||||
.P2
|
||||
.PP
|
||||
As we said earlier,
|
||||
there is a limit (typically 15-25)
|
||||
on the number of files which a program
|
||||
may have open simultaneously.
|
||||
Accordingly, any program which intends to process
|
||||
many files must be prepared to re-use
|
||||
file descriptors.
|
||||
The routine
|
||||
.UL close
|
||||
breaks the connection between a file descriptor
|
||||
and an open file,
|
||||
and frees the
|
||||
file descriptor for use with some other file.
|
||||
Termination of a program
|
||||
via
|
||||
.UL exit
|
||||
or return from the main program closes all open files.
|
||||
.PP
|
||||
The function
|
||||
.UL unlink(filename)
|
||||
removes the file
|
||||
.UL filename
|
||||
from the file system.
|
||||
.NH 2
|
||||
Random Access \(em Seek and Lseek
|
||||
.PP
|
||||
File I/O is normally sequential:
|
||||
each
|
||||
.UL read
|
||||
or
|
||||
.UL write
|
||||
takes place at a position in the file
|
||||
right after the previous one.
|
||||
When necessary, however,
|
||||
a file can be read or written in any arbitrary order.
|
||||
The
|
||||
system call
|
||||
.UL lseek
|
||||
provides a way to move around in
|
||||
a file without actually reading
|
||||
or writing:
|
||||
.P1
|
||||
lseek(fd, offset, origin);
|
||||
.P2
|
||||
forces the current position in the file
|
||||
whose descriptor is
|
||||
.UL fd
|
||||
to move to position
|
||||
.UL offset ,
|
||||
which is taken relative to the location
|
||||
specified by
|
||||
.UL origin .
|
||||
Subsequent reading or writing will begin at that position.
|
||||
.UL offset
|
||||
is
|
||||
a
|
||||
.UL long ;
|
||||
.UL fd
|
||||
and
|
||||
.UL origin
|
||||
are
|
||||
.UL int 's.
|
||||
.UL origin
|
||||
can be 0, 1, or 2 to specify that
|
||||
.UL offset
|
||||
is to be
|
||||
measured from
|
||||
the beginning, from the current position, or from the
|
||||
end of the file respectively.
|
||||
For example,
|
||||
to append to a file,
|
||||
seek to the end before writing:
|
||||
.P1
|
||||
lseek(fd, 0L, 2);
|
||||
.P2
|
||||
To get back to the beginning (``rewind''),
|
||||
.P1
|
||||
lseek(fd, 0L, 0);
|
||||
.P2
|
||||
Notice the
|
||||
.UL 0L
|
||||
argument;
|
||||
it could also be written as
|
||||
.UL (long)\ 0 .
|
||||
.PP
|
||||
With
|
||||
.UL lseek ,
|
||||
it is possible to treat files more or less like large arrays,
|
||||
at the price of slower access.
|
||||
For example, the following simple function reads any number of bytes
|
||||
from any arbitrary place in a file.
|
||||
.P1
|
||||
get(fd, pos, buf, n) /* read n bytes from position pos */
|
||||
int fd, n;
|
||||
long pos;
|
||||
char *buf;
|
||||
{
|
||||
lseek(fd, pos, 0); /* get to pos */
|
||||
return(read(fd, buf, n));
|
||||
}
|
||||
.P2
|
||||
.PP
|
||||
In pre-version 7
|
||||
.UC UNIX ,
|
||||
the basic entry point to the I/O system
|
||||
is called
|
||||
.UL seek .
|
||||
.UL seek
|
||||
is identical to
|
||||
.UL lseek ,
|
||||
except that its
|
||||
.UL offset
|
||||
argument is an
|
||||
.UL int
|
||||
rather than a
|
||||
.UL long .
|
||||
Accordingly,
|
||||
since
|
||||
.UC PDP -11
|
||||
integers have only 16 bits,
|
||||
the
|
||||
.UL offset
|
||||
specified
|
||||
for
|
||||
.UL seek
|
||||
is limited to 65,535;
|
||||
for this reason,
|
||||
.UL origin
|
||||
values of 3, 4, 5 cause
|
||||
.UL seek
|
||||
to multiply the given offset by 512
|
||||
(the number of bytes in one physical block)
|
||||
and then interpret
|
||||
.UL origin
|
||||
as if it were 0, 1, or 2 respectively.
|
||||
Thus to get to an arbitrary place in a large file
|
||||
requires two seeks, first one which selects
|
||||
the block, then one which
|
||||
has
|
||||
.UL origin
|
||||
equal to 1 and moves to the desired byte within the block.
|
||||
.NH 2
|
||||
Error Processing
|
||||
.PP
|
||||
The routines discussed in this section,
|
||||
and in fact all the routines which are direct entries into the system
|
||||
can incur errors.
|
||||
Usually they indicate an error by returning a value of \-1.
|
||||
Sometimes it is nice to know what sort of error occurred;
|
||||
for this purpose all these routines, when appropriate,
|
||||
leave an error number in the external cell
|
||||
.UL errno .
|
||||
The meanings of the various error numbers are
|
||||
listed
|
||||
in the introduction to Section II
|
||||
of the
|
||||
.I
|
||||
.UC UNIX
|
||||
Programmer's Manual,
|
||||
.R
|
||||
so your program can, for example, determine if
|
||||
an attempt to open a file failed because it did not exist
|
||||
or because the user lacked permission to read it.
|
||||
Perhaps more commonly,
|
||||
you may want to print out the
|
||||
reason for failure.
|
||||
The routine
|
||||
.UL perror
|
||||
will print a message associated with the value
|
||||
of
|
||||
.UL errno ;
|
||||
more generally,
|
||||
.UL sys\_errno
|
||||
is an array of character strings which can be indexed
|
||||
by
|
||||
.UL errno
|
||||
and printed by your program.
|
544
share/doc/psd/04.uprog/p5
Normal file
544
share/doc/psd/04.uprog/p5
Normal file
@ -0,0 +1,544 @@
|
||||
.\" This module is believed to contain source code proprietary to AT&T.
|
||||
.\" Use and redistribution is subject to the Berkeley Software License
|
||||
.\" Agreement and your Software Agreement with AT&T (Western Electric).
|
||||
.\"
|
||||
.\" @(#)p5 8.1 (Berkeley) 6/8/93
|
||||
.\"
|
||||
.\" $FreeBSD$
|
||||
.NH
|
||||
PROCESSES
|
||||
.PP
|
||||
It is often easier to use a program written
|
||||
by someone else than to invent one's own.
|
||||
This section describes how to
|
||||
execute a program from within another.
|
||||
.NH 2
|
||||
The ``System'' Function
|
||||
.PP
|
||||
The easiest way to execute a program from another
|
||||
is to use
|
||||
the standard library routine
|
||||
.UL system .
|
||||
.UL system
|
||||
takes one argument, a command string exactly as typed
|
||||
at the terminal
|
||||
(except for the newline at the end)
|
||||
and executes it.
|
||||
For instance, to time-stamp the output of a program,
|
||||
.P1
|
||||
main()
|
||||
{
|
||||
system("date");
|
||||
/* rest of processing */
|
||||
}
|
||||
.P2
|
||||
If the command string has to be built from pieces,
|
||||
the in-memory formatting capabilities of
|
||||
.UL sprintf
|
||||
may be useful.
|
||||
.PP
|
||||
Remember than
|
||||
.UL getc
|
||||
and
|
||||
.UL putc
|
||||
normally buffer their input;
|
||||
terminal I/O will not be properly synchronized unless
|
||||
this buffering is defeated.
|
||||
For output, use
|
||||
.UL fflush ;
|
||||
for input, see
|
||||
.UL setbuf
|
||||
in the appendix.
|
||||
.NH 2
|
||||
Low-Level Process Creation \(em Execl and Execv
|
||||
.PP
|
||||
If you're not using the standard library,
|
||||
or if you need finer control over what
|
||||
happens,
|
||||
you will have to construct calls to other programs
|
||||
using the more primitive routines that the standard
|
||||
library's
|
||||
.UL system
|
||||
routine is based on.
|
||||
.PP
|
||||
The most basic operation is to execute another program
|
||||
.ul
|
||||
without
|
||||
.IT returning ,
|
||||
by using the routine
|
||||
.UL execl .
|
||||
To print the date as the last action of a running program,
|
||||
use
|
||||
.P1
|
||||
execl("/bin/date", "date", NULL);
|
||||
.P2
|
||||
The first argument to
|
||||
.UL execl
|
||||
is the
|
||||
.ul
|
||||
file name
|
||||
of the command; you have to know where it is found
|
||||
in the file system.
|
||||
The second argument is conventionally
|
||||
the program name
|
||||
(that is, the last component of the file name),
|
||||
but this is seldom used except as a place-holder.
|
||||
If the command takes arguments, they are strung out after
|
||||
this;
|
||||
the end of the list is marked by a
|
||||
.UL NULL
|
||||
argument.
|
||||
.PP
|
||||
The
|
||||
.UL execl
|
||||
call
|
||||
overlays the existing program with
|
||||
the new one,
|
||||
runs that, then exits.
|
||||
There is
|
||||
.ul
|
||||
no
|
||||
return to the original program.
|
||||
.PP
|
||||
More realistically,
|
||||
a program might fall into two or more phases
|
||||
that communicate only through temporary files.
|
||||
Here it is natural to make the second pass
|
||||
simply an
|
||||
.UL execl
|
||||
call from the first.
|
||||
.PP
|
||||
The one exception to the rule that the original program never gets control
|
||||
back occurs when there is an error, for example if the file can't be found
|
||||
or is not executable.
|
||||
If you don't know where
|
||||
.UL date
|
||||
is located, say
|
||||
.P1
|
||||
execl("/bin/date", "date", NULL);
|
||||
execl("/usr/bin/date", "date", NULL);
|
||||
fprintf(stderr, "Someone stole 'date'\en");
|
||||
.P2
|
||||
.PP
|
||||
A variant of
|
||||
.UL execl
|
||||
called
|
||||
.UL execv
|
||||
is useful when you don't know in advance how many arguments there are going to be.
|
||||
The call is
|
||||
.P1
|
||||
execv(filename, argp);
|
||||
.P2
|
||||
where
|
||||
.UL argp
|
||||
is an array of pointers to the arguments;
|
||||
the last pointer in the array must be
|
||||
.UL NULL
|
||||
so
|
||||
.UL execv
|
||||
can tell where the list ends.
|
||||
As with
|
||||
.UL execl ,
|
||||
.UL filename
|
||||
is the file in which the program is found, and
|
||||
.UL argp[0]
|
||||
is the name of the program.
|
||||
(This arrangement is identical to the
|
||||
.UL argv
|
||||
array for program arguments.)
|
||||
.PP
|
||||
Neither of these routines provides the niceties of normal command execution.
|
||||
There is no automatic search of multiple directories \(em
|
||||
you have to know precisely where the command is located.
|
||||
Nor do you get the expansion of metacharacters like
|
||||
.UL < ,
|
||||
.UL > ,
|
||||
.UL * ,
|
||||
.UL ? ,
|
||||
and
|
||||
.UL []
|
||||
in the argument list.
|
||||
If you want these, use
|
||||
.UL execl
|
||||
to invoke the shell
|
||||
.UL sh ,
|
||||
which then does all the work.
|
||||
Construct a string
|
||||
.UL commandline
|
||||
that contains the complete command as it would have been typed
|
||||
at the terminal, then say
|
||||
.P1
|
||||
execl("/bin/sh", "sh", "-c", commandline, NULL);
|
||||
.P2
|
||||
The shell is assumed to be at a fixed place,
|
||||
.UL /bin/sh .
|
||||
Its argument
|
||||
.UL -c
|
||||
says to treat the next argument
|
||||
as a whole command line, so it does just what you want.
|
||||
The only problem is in constructing the right information
|
||||
in
|
||||
.UL commandline .
|
||||
.NH 2
|
||||
Control of Processes \(em Fork and Wait
|
||||
.PP
|
||||
So far what we've talked about isn't really all that useful by itself.
|
||||
Now we will show how to regain control after running
|
||||
a program with
|
||||
.UL execl
|
||||
or
|
||||
.UL execv .
|
||||
Since these routines simply overlay the new program on the old one,
|
||||
to save the old one requires that it first be split into
|
||||
two copies;
|
||||
one of these can be overlaid, while the other waits for the new,
|
||||
overlaying program to finish.
|
||||
The splitting is done by a routine called
|
||||
.UL fork :
|
||||
.P1
|
||||
proc_id = fork();
|
||||
.P2
|
||||
splits the program into two copies, both of which continue to run.
|
||||
The only difference between the two is the value of
|
||||
.UL proc_id ,
|
||||
the ``process id.''
|
||||
In one of these processes (the ``child''),
|
||||
.UL proc_id
|
||||
is zero.
|
||||
In the other
|
||||
(the ``parent''),
|
||||
.UL proc_id
|
||||
is non-zero; it is the process number of the child.
|
||||
Thus the basic way to call, and return from,
|
||||
another program is
|
||||
.P1
|
||||
if (fork() == 0)
|
||||
execl("/bin/sh", "sh", "-c", cmd, NULL); /* in child */
|
||||
.P2
|
||||
And in fact, except for handling errors, this is sufficient.
|
||||
The
|
||||
.UL fork
|
||||
makes two copies of the program.
|
||||
In the child, the value returned by
|
||||
.UL fork
|
||||
is zero, so it calls
|
||||
.UL execl
|
||||
which does the
|
||||
.UL command
|
||||
and then dies.
|
||||
In the parent,
|
||||
.UL fork
|
||||
returns non-zero
|
||||
so it skips the
|
||||
.UL execl.
|
||||
(If there is any error,
|
||||
.UL fork
|
||||
returns
|
||||
.UL -1 ).
|
||||
.PP
|
||||
More often, the parent wants to wait for the child to terminate
|
||||
before continuing itself.
|
||||
This can be done with
|
||||
the function
|
||||
.UL wait :
|
||||
.P1
|
||||
int status;
|
||||
|
||||
if (fork() == 0)
|
||||
execl(...);
|
||||
wait(&status);
|
||||
.P2
|
||||
This still doesn't handle any abnormal conditions, such as a failure
|
||||
of the
|
||||
.UL execl
|
||||
or
|
||||
.UL fork ,
|
||||
or the possibility that there might be more than one child running simultaneously.
|
||||
(The
|
||||
.UL wait
|
||||
returns the
|
||||
process id
|
||||
of the terminated child, if you want to check it against the value
|
||||
returned by
|
||||
.UL fork .)
|
||||
Finally, this fragment doesn't deal with any
|
||||
funny behavior on the part of the child
|
||||
(which is reported in
|
||||
.UL status ).
|
||||
Still, these three lines
|
||||
are the heart of the standard library's
|
||||
.UL system
|
||||
routine,
|
||||
which we'll show in a moment.
|
||||
.PP
|
||||
The
|
||||
.UL status
|
||||
returned by
|
||||
.UL wait
|
||||
encodes in its low-order eight bits
|
||||
the system's idea of the child's termination status;
|
||||
it is 0 for normal termination and non-zero to indicate
|
||||
various kinds of problems.
|
||||
The next higher eight bits are taken from the argument
|
||||
of the call to
|
||||
.UL exit
|
||||
which caused a normal termination of the child process.
|
||||
It is good coding practice
|
||||
for all programs to return meaningful
|
||||
status.
|
||||
.PP
|
||||
When a program is called by the shell,
|
||||
the three file descriptors
|
||||
0, 1, and 2 are set up pointing at the right files,
|
||||
and all other possible file descriptors
|
||||
are available for use.
|
||||
When this program calls another one,
|
||||
correct etiquette suggests making sure the same conditions
|
||||
hold.
|
||||
Neither
|
||||
.UL fork
|
||||
nor the
|
||||
.UL exec
|
||||
calls affects open files in any way.
|
||||
If the parent is buffering output
|
||||
that must come out before output from the child,
|
||||
the parent must flush its buffers
|
||||
before the
|
||||
.UL execl .
|
||||
Conversely,
|
||||
if a caller buffers an input stream,
|
||||
the called program will lose any information
|
||||
that has been read by the caller.
|
||||
.NH 2
|
||||
Pipes
|
||||
.PP
|
||||
A
|
||||
.ul
|
||||
pipe
|
||||
is an I/O channel intended for use
|
||||
between two cooperating processes:
|
||||
one process writes into the pipe,
|
||||
while the other reads.
|
||||
The system looks after buffering the data and synchronizing
|
||||
the two processes.
|
||||
Most pipes are created by the shell,
|
||||
as in
|
||||
.P1
|
||||
ls | pr
|
||||
.P2
|
||||
which connects the standard output of
|
||||
.UL ls
|
||||
to the standard input of
|
||||
.UL pr .
|
||||
Sometimes, however, it is most convenient
|
||||
for a process to set up its own plumbing;
|
||||
in this section, we will illustrate how
|
||||
the pipe connection is established and used.
|
||||
.PP
|
||||
The system call
|
||||
.UL pipe
|
||||
creates a pipe.
|
||||
Since a pipe is used for both reading and writing,
|
||||
two file descriptors are returned;
|
||||
the actual usage is like this:
|
||||
.P1
|
||||
int fd[2];
|
||||
|
||||
stat = pipe(fd);
|
||||
if (stat == -1)
|
||||
/* there was an error ... */
|
||||
.P2
|
||||
.UL fd
|
||||
is an array of two file descriptors, where
|
||||
.UL fd[0]
|
||||
is the read side of the pipe and
|
||||
.UL fd[1]
|
||||
is for writing.
|
||||
These may be used in
|
||||
.UL read ,
|
||||
.UL write
|
||||
and
|
||||
.UL close
|
||||
calls just like any other file descriptors.
|
||||
.PP
|
||||
If a process reads a pipe which is empty,
|
||||
it will wait until data arrives;
|
||||
if a process writes into a pipe which
|
||||
is too full, it will wait until the pipe empties somewhat.
|
||||
If the write side of the pipe is closed,
|
||||
a subsequent
|
||||
.UL read
|
||||
will encounter end of file.
|
||||
.PP
|
||||
To illustrate the use of pipes in a realistic setting,
|
||||
let us write a function called
|
||||
.UL popen(cmd,\ mode) ,
|
||||
which creates a process
|
||||
.UL cmd
|
||||
(just as
|
||||
.UL system
|
||||
does),
|
||||
and returns a file descriptor that will either
|
||||
read or write that process, according to
|
||||
.UL mode .
|
||||
That is,
|
||||
the call
|
||||
.P1
|
||||
fout = popen("pr", WRITE);
|
||||
.P2
|
||||
creates a process that executes
|
||||
the
|
||||
.UL pr
|
||||
command;
|
||||
subsequent
|
||||
.UL write
|
||||
calls using the file descriptor
|
||||
.UL fout
|
||||
will send their data to that process
|
||||
through the pipe.
|
||||
.PP
|
||||
.UL popen
|
||||
first creates the
|
||||
the pipe with a
|
||||
.UL pipe
|
||||
system call;
|
||||
it then
|
||||
.UL fork s
|
||||
to create two copies of itself.
|
||||
The child decides whether it is supposed to read or write,
|
||||
closes the other side of the pipe,
|
||||
then calls the shell (via
|
||||
.UL execl )
|
||||
to run the desired process.
|
||||
The parent likewise closes the end of the pipe it does not use.
|
||||
These closes are necessary to make end-of-file tests work properly.
|
||||
For example, if a child that intends to read
|
||||
fails to close the write end of the pipe, it will never
|
||||
see the end of the pipe file, just because there is one writer
|
||||
potentially active.
|
||||
.P1
|
||||
#include <stdio.h>
|
||||
|
||||
#define READ 0
|
||||
#define WRITE 1
|
||||
#define tst(a, b) (mode == READ ? (b) : (a))
|
||||
static int popen_pid;
|
||||
|
||||
popen(cmd, mode)
|
||||
char *cmd;
|
||||
int mode;
|
||||
{
|
||||
int p[2];
|
||||
|
||||
if (pipe(p) < 0)
|
||||
return(NULL);
|
||||
if ((popen_pid = fork()) == 0) {
|
||||
close(tst(p[WRITE], p[READ]));
|
||||
close(tst(0, 1));
|
||||
dup(tst(p[READ], p[WRITE]));
|
||||
close(tst(p[READ], p[WRITE]));
|
||||
execl("/bin/sh", "sh", "-c", cmd, 0);
|
||||
_exit(1); /* disaster has occurred if we get here */
|
||||
}
|
||||
if (popen_pid == -1)
|
||||
return(NULL);
|
||||
close(tst(p[READ], p[WRITE]));
|
||||
return(tst(p[WRITE], p[READ]));
|
||||
}
|
||||
.P2
|
||||
The sequence of
|
||||
.UL close s
|
||||
in the child
|
||||
is a bit tricky.
|
||||
Suppose
|
||||
that the task is to create a child process that will read data from the parent.
|
||||
Then the first
|
||||
.UL close
|
||||
closes the write side of the pipe,
|
||||
leaving the read side open.
|
||||
The lines
|
||||
.P1
|
||||
close(tst(0, 1));
|
||||
dup(tst(p[READ], p[WRITE]));
|
||||
.P2
|
||||
are the conventional way to associate the pipe descriptor
|
||||
with the standard input of the child.
|
||||
The
|
||||
.UL close
|
||||
closes file descriptor 0,
|
||||
that is, the standard input.
|
||||
.UL dup
|
||||
is a system call that
|
||||
returns a duplicate of an already open file descriptor.
|
||||
File descriptors are assigned in increasing order
|
||||
and the first available one is returned,
|
||||
so
|
||||
the effect of the
|
||||
.UL dup
|
||||
is to copy the file descriptor for the pipe (read side)
|
||||
to file descriptor 0;
|
||||
thus the read side of the pipe becomes the standard input.
|
||||
(Yes, this is a bit tricky, but it's a standard idiom.)
|
||||
Finally, the old read side of the pipe is closed.
|
||||
.PP
|
||||
A similar sequence of operations takes place
|
||||
when the child process is supposed to write
|
||||
from the parent instead of reading.
|
||||
You may find it a useful exercise to step through that case.
|
||||
.PP
|
||||
The job is not quite done,
|
||||
for we still need a function
|
||||
.UL pclose
|
||||
to close the pipe created by
|
||||
.UL popen .
|
||||
The main reason for using a separate function rather than
|
||||
.UL close
|
||||
is that it is desirable to wait for the termination of the child process.
|
||||
First, the return value from
|
||||
.UL pclose
|
||||
indicates whether the process succeeded.
|
||||
Equally important when a process creates several children
|
||||
is that only a bounded number of unwaited-for children
|
||||
can exist, even if some of them have terminated;
|
||||
performing the
|
||||
.UL wait
|
||||
lays the child to rest.
|
||||
Thus:
|
||||
.P1
|
||||
#include <signal.h>
|
||||
|
||||
pclose(fd) /* close pipe fd */
|
||||
int fd;
|
||||
{
|
||||
register r, (*hstat)(), (*istat)(), (*qstat)();
|
||||
int status;
|
||||
extern int popen_pid;
|
||||
|
||||
close(fd);
|
||||
istat = signal(SIGINT, SIG_IGN);
|
||||
qstat = signal(SIGQUIT, SIG_IGN);
|
||||
hstat = signal(SIGHUP, SIG_IGN);
|
||||
while ((r = wait(&status)) != popen_pid && r != -1);
|
||||
if (r == -1)
|
||||
status = -1;
|
||||
signal(SIGINT, istat);
|
||||
signal(SIGQUIT, qstat);
|
||||
signal(SIGHUP, hstat);
|
||||
return(status);
|
||||
}
|
||||
.P2
|
||||
The calls to
|
||||
.UL signal
|
||||
make sure that no interrupts, etc.,
|
||||
interfere with the waiting process;
|
||||
this is the topic of the next section.
|
||||
.PP
|
||||
The routine as written has the limitation that only one pipe may
|
||||
be open at once, because of the single shared variable
|
||||
.UL popen_pid ;
|
||||
it really should be an array indexed by file descriptor.
|
||||
A
|
||||
.UL popen
|
||||
function, with slightly different arguments and return value is available
|
||||
as part of the standard I/O library discussed below.
|
||||
As currently written, it shares the same limitation.
|
328
share/doc/psd/04.uprog/p6
Normal file
328
share/doc/psd/04.uprog/p6
Normal file
@ -0,0 +1,328 @@
|
||||
.\" This module is believed to contain source code proprietary to AT&T.
|
||||
.\" Use and redistribution is subject to the Berkeley Software License
|
||||
.\" Agreement and your Software Agreement with AT&T (Western Electric).
|
||||
.\"
|
||||
.\" @(#)p6 8.1 (Berkeley) 6/8/93
|
||||
.\"
|
||||
.\" $FreeBSD$
|
||||
.NH
|
||||
SIGNALS \(em INTERRUPTS AND ALL THAT
|
||||
.PP
|
||||
This section is concerned with how to
|
||||
deal gracefully with signals from
|
||||
the outside world (like interrupts), and with program faults.
|
||||
Since there's nothing very useful that
|
||||
can be done from within C about program
|
||||
faults, which arise mainly from illegal memory references
|
||||
or from execution of peculiar instructions,
|
||||
we'll discuss only the outside-world signals:
|
||||
.IT interrupt ,
|
||||
which is sent when the
|
||||
.UC DEL
|
||||
character is typed;
|
||||
.IT quit ,
|
||||
generated by the
|
||||
.UC FS
|
||||
character;
|
||||
.IT hangup ,
|
||||
caused by hanging up the phone;
|
||||
and
|
||||
.IT terminate ,
|
||||
generated by the
|
||||
.IT kill
|
||||
command.
|
||||
When one of these events occurs,
|
||||
the signal is sent to
|
||||
.IT all
|
||||
processes which were started
|
||||
from the corresponding terminal;
|
||||
unless other arrangements have been made,
|
||||
the signal
|
||||
terminates the process.
|
||||
In the
|
||||
.IT quit
|
||||
case, a core image file is written for debugging
|
||||
purposes.
|
||||
.PP
|
||||
The routine which alters the default action
|
||||
is
|
||||
called
|
||||
.UL signal .
|
||||
It has two arguments: the first specifies the signal, and the second
|
||||
specifies how to treat it.
|
||||
The first argument is just a number code, but the second is the
|
||||
address is either a function, or a somewhat strange code
|
||||
that requests that the signal either be ignored, or that it be
|
||||
given the default action.
|
||||
The include file
|
||||
.UL signal.h
|
||||
gives names for the various arguments, and should always be included
|
||||
when signals are used.
|
||||
Thus
|
||||
.P1
|
||||
#include <signal.h>
|
||||
...
|
||||
signal(SIGINT, SIG_IGN);
|
||||
.P2
|
||||
causes interrupts to be ignored, while
|
||||
.P1
|
||||
signal(SIGINT, SIG_DFL);
|
||||
.P2
|
||||
restores the default action of process termination.
|
||||
In all cases,
|
||||
.UL signal
|
||||
returns the previous value of the signal.
|
||||
The second argument to
|
||||
.UL signal
|
||||
may instead be the name of a function
|
||||
(which has to be declared explicitly if
|
||||
the compiler hasn't seen it already).
|
||||
In this case, the named routine will be called
|
||||
when the signal occurs.
|
||||
Most commonly this facility is used
|
||||
to allow the program to clean up
|
||||
unfinished business before terminating, for example to
|
||||
delete a temporary file:
|
||||
.P1
|
||||
#include <signal.h>
|
||||
|
||||
main()
|
||||
{
|
||||
int onintr();
|
||||
|
||||
if (signal(SIGINT, SIG_IGN) != SIG_IGN)
|
||||
signal(SIGINT, onintr);
|
||||
|
||||
/* Process ... */
|
||||
|
||||
exit(0);
|
||||
}
|
||||
|
||||
onintr()
|
||||
{
|
||||
unlink(tempfile);
|
||||
exit(1);
|
||||
}
|
||||
.P2
|
||||
.PP
|
||||
Why the test and the double call to
|
||||
.UL signal ?
|
||||
Recall that signals like interrupt are sent to
|
||||
.ul
|
||||
all
|
||||
processes started from a particular terminal.
|
||||
Accordingly, when a program is to be run
|
||||
non-interactively
|
||||
(started by
|
||||
.UL & ),
|
||||
the shell turns off interrupts for it
|
||||
so it won't be stopped by interrupts intended for foreground processes.
|
||||
If this program began by announcing that all interrupts were to be sent
|
||||
to the
|
||||
.UL onintr
|
||||
routine regardless,
|
||||
that would undo the shell's effort to protect it
|
||||
when run in the background.
|
||||
.PP
|
||||
The solution, shown above, is to test the state of interrupt handling,
|
||||
and to continue to ignore interrupts if they are already being ignored.
|
||||
The code as written
|
||||
depends on the fact that
|
||||
.UL signal
|
||||
returns the previous state of a particular signal.
|
||||
If signals were already being ignored, the process should continue to ignore them;
|
||||
otherwise, they should be caught.
|
||||
.PP
|
||||
A more sophisticated program may wish to intercept
|
||||
an interrupt and interpret it as a request
|
||||
to stop what it is doing
|
||||
and return to its own command-processing loop.
|
||||
Think of a text editor:
|
||||
interrupting a long printout should not cause it
|
||||
to terminate and lose the work
|
||||
already done.
|
||||
The outline of the code for this case is probably best written like this:
|
||||
.P1
|
||||
#include <signal.h>
|
||||
#include <setjmp.h>
|
||||
jmp_buf sjbuf;
|
||||
|
||||
main()
|
||||
{
|
||||
int (*istat)(), onintr();
|
||||
|
||||
istat = signal(SIGINT, SIG_IGN); /* save original status */
|
||||
setjmp(sjbuf); /* save current stack position */
|
||||
if (istat != SIG_IGN)
|
||||
signal(SIGINT, onintr);
|
||||
|
||||
/* main processing loop */
|
||||
}
|
||||
.P2
|
||||
.P1
|
||||
onintr()
|
||||
{
|
||||
printf("\enInterrupt\en");
|
||||
longjmp(sjbuf); /* return to saved state */
|
||||
}
|
||||
.P2
|
||||
The include file
|
||||
.UL setjmp.h
|
||||
declares the type
|
||||
.UL jmp_buf
|
||||
an object in which the state
|
||||
can be saved.
|
||||
.UL sjbuf
|
||||
is such an object; it is an array of some sort.
|
||||
The
|
||||
.UL setjmp
|
||||
routine then saves
|
||||
the state of things.
|
||||
When an interrupt occurs,
|
||||
a call is forced to the
|
||||
.UL onintr
|
||||
routine,
|
||||
which can print a message, set flags, or whatever.
|
||||
.UL longjmp
|
||||
takes as argument an object stored into by
|
||||
.UL setjmp ,
|
||||
and restores control
|
||||
to the location after the call to
|
||||
.UL setjmp ,
|
||||
so control (and the stack level) will pop back
|
||||
to the place in the main routine where
|
||||
the signal is set up and the main loop entered.
|
||||
Notice, by the way, that
|
||||
the signal
|
||||
gets set again after an interrupt occurs.
|
||||
This is necessary; most signals are automatically
|
||||
reset to their default action when they occur.
|
||||
.PP
|
||||
Some programs that want to detect signals simply can't be stopped
|
||||
at an arbitrary point,
|
||||
for example in the middle of updating a linked list.
|
||||
If the routine called on occurrence of a signal
|
||||
sets a flag and then
|
||||
returns instead of calling
|
||||
.UL exit
|
||||
or
|
||||
.UL longjmp ,
|
||||
execution will continue
|
||||
at the exact point it was interrupted.
|
||||
The interrupt flag can then be tested later.
|
||||
.PP
|
||||
There is one difficulty associated with this
|
||||
approach.
|
||||
Suppose the program is reading the
|
||||
terminal when the interrupt is sent.
|
||||
The specified routine is duly called; it sets its flag
|
||||
and returns.
|
||||
If it were really true, as we said
|
||||
above, that ``execution resumes at the exact point it was interrupted,''
|
||||
the program would continue reading the terminal
|
||||
until the user typed another line.
|
||||
This behavior might well be confusing, since the user
|
||||
might not know that the program is reading;
|
||||
he presumably would prefer to have the signal take effect instantly.
|
||||
The method chosen to resolve this difficulty
|
||||
is to terminate the terminal read when execution
|
||||
resumes after the signal, returning an error code
|
||||
which indicates what happened.
|
||||
.PP
|
||||
Thus programs which catch and resume
|
||||
execution after signals should be prepared for ``errors''
|
||||
which are caused by interrupted
|
||||
system calls.
|
||||
(The ones to watch out for are reads from a terminal,
|
||||
.UL wait ,
|
||||
and
|
||||
.UL pause .)
|
||||
A program
|
||||
whose
|
||||
.UL onintr
|
||||
program just sets
|
||||
.UL intflag ,
|
||||
resets the interrupt signal, and returns,
|
||||
should usually include code like the following when it reads
|
||||
the standard input:
|
||||
.P1
|
||||
if (getchar() == EOF)
|
||||
if (intflag)
|
||||
/* EOF caused by interrupt */
|
||||
else
|
||||
/* true end-of-file */
|
||||
.P2
|
||||
.PP
|
||||
A final subtlety to keep in mind becomes important
|
||||
when signal-catching is combined with execution of other programs.
|
||||
Suppose a program catches interrupts, and also includes
|
||||
a method (like ``!'' in the editor)
|
||||
whereby other programs can be executed.
|
||||
Then the code should look something like this:
|
||||
.P1
|
||||
if (fork() == 0)
|
||||
execl(...);
|
||||
signal(SIGINT, SIG_IGN); /* ignore interrupts */
|
||||
wait(&status); /* until the child is done */
|
||||
signal(SIGINT, onintr); /* restore interrupts */
|
||||
.P2
|
||||
Why is this?
|
||||
Again, it's not obvious but not really difficult.
|
||||
Suppose the program you call catches its own interrupts.
|
||||
If you interrupt the subprogram,
|
||||
it will get the signal and return to its
|
||||
main loop, and probably read your terminal.
|
||||
But the calling program will also pop out of
|
||||
its wait for the subprogram and read your terminal.
|
||||
Having two processes reading
|
||||
your terminal is very unfortunate,
|
||||
since the system figuratively flips a coin to decide
|
||||
who should get each line of input.
|
||||
A simple way out is to have the parent program
|
||||
ignore interrupts until the child is done.
|
||||
This reasoning is reflected in the standard I/O library function
|
||||
.UL system :
|
||||
.P1
|
||||
#include <signal.h>
|
||||
|
||||
system(s) /* run command string s */
|
||||
char *s;
|
||||
{
|
||||
int status, pid, w;
|
||||
register int (*istat)(), (*qstat)();
|
||||
|
||||
if ((pid = fork()) == 0) {
|
||||
execl("/bin/sh", "sh", "-c", s, 0);
|
||||
_exit(127);
|
||||
}
|
||||
istat = signal(SIGINT, SIG_IGN);
|
||||
qstat = signal(SIGQUIT, SIG_IGN);
|
||||
while ((w = wait(&status)) != pid && w != -1)
|
||||
;
|
||||
if (w == -1)
|
||||
status = -1;
|
||||
signal(SIGINT, istat);
|
||||
signal(SIGQUIT, qstat);
|
||||
return(status);
|
||||
}
|
||||
.P2
|
||||
.PP
|
||||
As an aside on declarations,
|
||||
the function
|
||||
.UL signal
|
||||
obviously has a rather strange second argument.
|
||||
It is in fact a pointer to a function delivering an integer,
|
||||
and this is also the type of the signal routine itself.
|
||||
The two values
|
||||
.UL SIG_IGN
|
||||
and
|
||||
.UL SIG_DFL
|
||||
have the right type, but are chosen so they coincide with
|
||||
no possible actual functions.
|
||||
For the enthusiast, here is how they are defined for the PDP-11;
|
||||
the definitions should be sufficiently ugly
|
||||
and nonportable to encourage use of the include file.
|
||||
.P1
|
||||
#define SIG_DFL (int (*)())0
|
||||
#define SIG_IGN (int (*)())1
|
||||
.P2
|
29
share/doc/psd/04.uprog/p8
Normal file
29
share/doc/psd/04.uprog/p8
Normal file
@ -0,0 +1,29 @@
|
||||
.\" This module is believed to contain source code proprietary to AT&T.
|
||||
.\" Use and redistribution is subject to the Berkeley Software License
|
||||
.\" Agreement and your Software Agreement with AT&T (Western Electric).
|
||||
.\"
|
||||
.\" @(#)p8 8.1 (Berkeley) 6/8/93
|
||||
.\"
|
||||
.\" $FreeBSD$
|
||||
.SH
|
||||
References
|
||||
.LP
|
||||
.IP [1]
|
||||
K. L. Thompson and D. M. Ritchie,
|
||||
.ul
|
||||
The
|
||||
.ul
|
||||
.UC UNIX
|
||||
.ul
|
||||
Programmer's Manual,
|
||||
Bell Laboratories, 1978.
|
||||
.IP [2]
|
||||
B. W. Kernighan and D. M. Ritchie,
|
||||
.ul
|
||||
The C Programming Language,
|
||||
Prentice-Hall, Inc., 1978.
|
||||
.IP [3]
|
||||
B. W. Kernighan,
|
||||
.UC UNIX \& ``
|
||||
for Beginners \(em Second Edition.''
|
||||
Bell Laboratories, 1978.
|
647
share/doc/psd/04.uprog/p9
Normal file
647
share/doc/psd/04.uprog/p9
Normal file
@ -0,0 +1,647 @@
|
||||
.\" This module is believed to contain source code proprietary to AT&T.
|
||||
.\" Use and redistribution is subject to the Berkeley Software License
|
||||
.\" Agreement and your Software Agreement with AT&T (Western Electric).
|
||||
.\"
|
||||
.\" @(#)p9 8.1 (Berkeley) 6/8/93
|
||||
.\"
|
||||
.\" $FreeBSD$
|
||||
.sp 100
|
||||
.TL
|
||||
.ft R
|
||||
Appendix \(em The Standard I/O Library
|
||||
.AU
|
||||
D. M. Ritchie
|
||||
.AI
|
||||
AT&T Bell Laboratories
|
||||
Murray Hill, NJ 07974
|
||||
.PP
|
||||
The standard I/O library
|
||||
was designed with the following goals in mind.
|
||||
.IP 1.
|
||||
It must be as efficient as possible, both in time and in space,
|
||||
so that there will be no hesitation in using it
|
||||
no matter how critical the application.
|
||||
.IP 2.
|
||||
It must be simple to use, and also free of the magic
|
||||
numbers and mysterious calls
|
||||
whose use mars the understandability and portability
|
||||
of many programs using older packages.
|
||||
.IP 3.
|
||||
The interface provided should be applicable on all machines,
|
||||
whether or not the programs which implement it are directly portable
|
||||
to other systems,
|
||||
or to machines other than the PDP-11 running a version of
|
||||
.UC UNIX .
|
||||
.SH
|
||||
1. General Usage
|
||||
.PP
|
||||
Each program using the library must have the line
|
||||
.P1
|
||||
#include <stdio.h>
|
||||
.P2
|
||||
which defines certain macros and variables.
|
||||
The routines are in the normal C library,
|
||||
so no special library argument is needed for loading.
|
||||
All names in the include file intended only for internal use begin
|
||||
with an underscore
|
||||
.UL _
|
||||
to reduce the possibility
|
||||
of collision with a user name.
|
||||
The names intended to be visible outside the package are
|
||||
.IP \f3stdin\f1 10
|
||||
The name of the standard input file
|
||||
.IP \f3stdout\f1 10
|
||||
The name of the standard output file
|
||||
.IP \f3stderr\f1 10
|
||||
The name of the standard error file
|
||||
.IP \f3EOF\f1 10
|
||||
is actually \-1, and is the value returned by
|
||||
the read routines on end-of-file or error.
|
||||
.IP \f3NULL\f1 10
|
||||
is a notation for the null pointer, returned by
|
||||
pointer-valued functions
|
||||
to indicate an error
|
||||
.IP \f3FILE\f1 10
|
||||
expands to
|
||||
.UL struct
|
||||
.UL _iob
|
||||
and is a useful
|
||||
shorthand when declaring pointers
|
||||
to streams.
|
||||
.IP \f3BUFSIZ\f1 10
|
||||
is a number (viz. 512)
|
||||
of the size suitable for an I/O buffer supplied by the user.
|
||||
See
|
||||
.UL setbuf ,
|
||||
below.
|
||||
.IP \f3getc,\ getchar,\ putc,\ putchar,\ feof,\ ferror,\ f\&ileno\f1 10
|
||||
.br
|
||||
are defined as macros.
|
||||
Their actions are described below;
|
||||
they are mentioned here
|
||||
to point out that it is not possible to
|
||||
redeclare them
|
||||
and that they are not actually functions;
|
||||
thus, for example, they may not have breakpoints set on them.
|
||||
.PP
|
||||
The routines in this package
|
||||
offer the convenience of automatic buffer allocation
|
||||
and output flushing where appropriate.
|
||||
The names
|
||||
.UL stdin ,
|
||||
.UL stdout ,
|
||||
and
|
||||
.UL stderr
|
||||
are in effect constants and may not be assigned to.
|
||||
.SH
|
||||
2. Calls
|
||||
.nr PD .4v
|
||||
.LP
|
||||
.UL FILE\ *fopen(filename,\ type)\ char\ *filename,\ *type;
|
||||
.nr PD 0
|
||||
.IP
|
||||
.br
|
||||
opens the file and, if needed, allocates a buffer for it.
|
||||
.UL filename
|
||||
is a character string specifying the name.
|
||||
.UL type
|
||||
is a character string (not a single character).
|
||||
It may be
|
||||
.UL \&"r" ,
|
||||
.UL \&"w" ,
|
||||
or
|
||||
.UL \&"a"
|
||||
to indicate
|
||||
intent to read, write, or append.
|
||||
The value returned is a file pointer.
|
||||
If it is
|
||||
.UL NULL
|
||||
the attempt to open failed.
|
||||
.ne 3
|
||||
.nr PD .4v
|
||||
.LP
|
||||
.UL FILE\ *freopen(filename,\ type,\ ioptr)\ char\ *filename,\ *type;\ FILE\ *ioptr;
|
||||
.nr PD 0
|
||||
.IP
|
||||
.br
|
||||
The stream named by
|
||||
.UL ioptr
|
||||
is closed, if necessary, and then reopened
|
||||
as if by
|
||||
.UL fopen .
|
||||
If the attempt to open fails,
|
||||
.UL NULL
|
||||
is returned,
|
||||
otherwise
|
||||
.UL ioptr ,
|
||||
which will now refer to the new file.
|
||||
Often the reopened stream is
|
||||
.UL stdin
|
||||
or
|
||||
.UL stdout .
|
||||
.nr PD .4v
|
||||
.LP
|
||||
.UL int\ getc(ioptr)\ FILE\ *ioptr;
|
||||
.nr PD 0
|
||||
.IP
|
||||
returns the next character from the stream named by
|
||||
.UL ioptr ,
|
||||
which is a pointer to a file such as returned by
|
||||
.UL fopen ,
|
||||
or the name
|
||||
.UL stdin .
|
||||
The integer
|
||||
.UL EOF
|
||||
is returned on end-of-file or when
|
||||
an error occurs.
|
||||
The null character
|
||||
.UL \e0
|
||||
is a legal character.
|
||||
.nr PD .4v
|
||||
.LP
|
||||
.UL int\ fgetc(ioptr)\ FILE\ *ioptr;
|
||||
.nr PD 0
|
||||
.IP
|
||||
.br
|
||||
acts like
|
||||
.UL getc
|
||||
but is a genuine function,
|
||||
not a macro,
|
||||
so it can be pointed to, passed as an argument, etc.
|
||||
.nr PD .4v
|
||||
.LP
|
||||
.UL putc(c,\ ioptr)\ FILE\ *ioptr;
|
||||
.nr PD 0
|
||||
.IP
|
||||
.br
|
||||
.UL putc
|
||||
writes the character
|
||||
.UL c
|
||||
on the output stream named by
|
||||
.UL ioptr ,
|
||||
which is a value returned from
|
||||
.UL fopen
|
||||
or perhaps
|
||||
.UL stdout
|
||||
or
|
||||
.UL stderr .
|
||||
The character is returned as value,
|
||||
but
|
||||
.UL EOF
|
||||
is returned on error.
|
||||
.nr PD .4v
|
||||
.LP
|
||||
.UL fputc(c,\ ioptr)\ FILE\ *ioptr;
|
||||
.nr PD 0
|
||||
.IP
|
||||
.br
|
||||
acts like
|
||||
.UL putc
|
||||
but is a genuine
|
||||
function, not a macro.
|
||||
.nr PD .4v
|
||||
.LP
|
||||
.UL fclose(ioptr)\ FILE\ *ioptr;
|
||||
.nr PD 0
|
||||
.IP
|
||||
.br
|
||||
The file corresponding to
|
||||
.UL ioptr
|
||||
is closed after any buffers are emptied.
|
||||
A buffer allocated by the I/O system is freed.
|
||||
.UL fclose
|
||||
is automatic on normal termination of the program.
|
||||
.nr PD .4v
|
||||
.LP
|
||||
.UL fflush(ioptr)\ FILE\ *ioptr;
|
||||
.nr PD 0
|
||||
.IP
|
||||
.br
|
||||
Any buffered information on the (output) stream named by
|
||||
.UL ioptr
|
||||
is written out.
|
||||
Output files are normally buffered
|
||||
if and only if they are not directed to the terminal;
|
||||
however,
|
||||
.UL stderr
|
||||
always starts off unbuffered and remains so unless
|
||||
.UL setbuf
|
||||
is used, or unless it is reopened.
|
||||
.nr PD .4v
|
||||
.LP
|
||||
.UL exit(errcode);
|
||||
.nr PD 0
|
||||
.IP
|
||||
.br
|
||||
terminates the process and returns its argument as status
|
||||
to the parent.
|
||||
This is a special version of the routine
|
||||
which calls
|
||||
.UL fflush
|
||||
for each output file.
|
||||
To terminate without flushing,
|
||||
use
|
||||
.UL _exit .
|
||||
.nr PD .4v
|
||||
.LP
|
||||
.UL feof(ioptr)\ FILE\ *ioptr;
|
||||
.nr PD 0
|
||||
.IP
|
||||
.br
|
||||
returns non-zero when end-of-file
|
||||
has occurred on the specified input stream.
|
||||
.nr PD .4v
|
||||
.LP
|
||||
.UL ferror(ioptr)\ FILE\ *ioptr;
|
||||
.nr PD 0
|
||||
.IP
|
||||
.br
|
||||
returns non-zero when an error has occurred while reading
|
||||
or writing the named stream.
|
||||
The error indication lasts until the file has been closed.
|
||||
.nr PD .4v
|
||||
.LP
|
||||
.UL getchar();
|
||||
.nr PD 0
|
||||
.IP
|
||||
.br
|
||||
is identical to
|
||||
.UL getc(stdin) .
|
||||
.nr PD .4v
|
||||
.LP
|
||||
.UL putchar(c);
|
||||
.nr PD 0
|
||||
.IP
|
||||
.br
|
||||
is identical to
|
||||
.UL putc(c,\ stdout) .
|
||||
.nr PD .4v
|
||||
.nr PD .4v
|
||||
.ne 2
|
||||
.LP
|
||||
.UL char\ *fgets(s,\ n,\ ioptr)\ char\ *s;\ FILE\ *ioptr;
|
||||
.nr PD 0
|
||||
.IP
|
||||
.br
|
||||
reads up to
|
||||
.UL n-1
|
||||
characters from the stream
|
||||
.UL ioptr
|
||||
into the character pointer
|
||||
.UL s .
|
||||
The read terminates with a newline character.
|
||||
The newline character is placed in the buffer
|
||||
followed by a null character.
|
||||
.UL fgets
|
||||
returns the first argument,
|
||||
or
|
||||
.UL NULL
|
||||
if error or end-of-file occurred.
|
||||
.nr PD .4v
|
||||
.nr PD .4v
|
||||
.LP
|
||||
.UL fputs(s,\ ioptr)\ char\ *s;\ FILE\ *ioptr;
|
||||
.nr PD 0
|
||||
.IP
|
||||
.br
|
||||
writes the null-terminated string (character array)
|
||||
.UL s
|
||||
on the stream
|
||||
.UL ioptr .
|
||||
No newline is appended.
|
||||
No value is returned.
|
||||
.nr PD .4v
|
||||
.LP
|
||||
.UL ungetc(c,\ ioptr)\ FILE\ *ioptr;
|
||||
.nr PD 0
|
||||
.IP
|
||||
.br
|
||||
The argument character
|
||||
.UL c
|
||||
is pushed back on the input stream named by
|
||||
.UL ioptr .
|
||||
Only one character may be pushed back.
|
||||
.ne 5
|
||||
.nr PD .4v
|
||||
.LP
|
||||
.UL printf(format,\ a1,\ ...)\ char\ *format;
|
||||
.br
|
||||
.UL fprintf(ioptr,\ format,\ a1,\ ...)\ FILE\ *ioptr;\ char\ *format;
|
||||
.br
|
||||
.UL sprintf(s,\ format,\ a1,\ ...)char\ *s,\ *format;
|
||||
.br
|
||||
.nr PD 0
|
||||
.IP
|
||||
.UL printf
|
||||
writes on the standard output.
|
||||
.UL fprintf
|
||||
writes on the named output stream.
|
||||
.UL sprintf
|
||||
puts characters in the character array (string)
|
||||
named by
|
||||
.UL s .
|
||||
The specifications are as described in section
|
||||
.UL printf (3)
|
||||
of the
|
||||
.ul
|
||||
.UC UNIX
|
||||
.ul
|
||||
Programmer's Manual.
|
||||
.nr PD .4v
|
||||
.LP
|
||||
.UL scanf(format,\ a1,\ ...)\ char\ *format;
|
||||
.br
|
||||
.UL fscanf(ioptr,\ format,\ a1,\ ...)\ FILE\ *ioptr;\ char\ *format;
|
||||
.br
|
||||
.UL sscanf(s,\ format,\ a1,\ ...)\ char\ *s,\ *format;
|
||||
.nr PD 0
|
||||
.IP
|
||||
.br
|
||||
.UL scanf
|
||||
reads from the standard input.
|
||||
.UL fscanf
|
||||
reads from the named input stream.
|
||||
.UL sscanf
|
||||
reads from the character string
|
||||
supplied as
|
||||
.UL s .
|
||||
.UL scanf
|
||||
reads characters, interprets
|
||||
them according to a format, and stores the results in its arguments.
|
||||
Each routine expects as arguments
|
||||
a control string
|
||||
.UL format ,
|
||||
and a set of arguments,
|
||||
.I
|
||||
each of which must be a pointer,
|
||||
.R
|
||||
indicating where the converted input should be stored.
|
||||
.if t .sp .4v
|
||||
.UL scanf
|
||||
returns as its value the number of successfully matched and assigned input
|
||||
items.
|
||||
This can be used to decide how many input items were found.
|
||||
On end of file,
|
||||
.UL EOF
|
||||
is returned; note that this is different
|
||||
from 0, which means that the next input character does not
|
||||
match what was called for in the control string.
|
||||
.nr PD .4v
|
||||
.LP
|
||||
.UL fread(ptr,\ sizeof(*ptr),\ nitems,\ ioptr)\ FILE\ *ioptr;
|
||||
.nr PD 0
|
||||
.IP
|
||||
.br
|
||||
reads
|
||||
.UL nitems
|
||||
of data beginning at
|
||||
.UL ptr
|
||||
from file
|
||||
.UL ioptr .
|
||||
No advance notification
|
||||
that binary I/O is being done is required;
|
||||
when, for portability reasons,
|
||||
it becomes required, it will be done
|
||||
by adding an additional character to the mode-string on the
|
||||
.UL fopen
|
||||
call.
|
||||
.nr PD .4v
|
||||
.LP
|
||||
.UL fwrite(ptr,\ sizeof(*ptr),\ nitems,\ ioptr)\ FILE\ *ioptr;
|
||||
.nr PD 0
|
||||
.IP
|
||||
.br
|
||||
Like
|
||||
.UL fread ,
|
||||
but in the other direction.
|
||||
.nr PD .4v
|
||||
.LP
|
||||
.UL rewind(ioptr)\ FILE\ *ioptr;
|
||||
.nr PD 0
|
||||
.IP
|
||||
.br
|
||||
rewinds the stream
|
||||
named by
|
||||
.UL ioptr .
|
||||
It is not very useful except on input,
|
||||
since a rewound output file is still open only for output.
|
||||
.nr PD .4v
|
||||
.LP
|
||||
.UL system(string)\ char\ *string;
|
||||
.nr PD 0
|
||||
.IP
|
||||
.br
|
||||
The
|
||||
.UL string
|
||||
is executed by the shell as if typed at the terminal.
|
||||
.nr PD .4v
|
||||
.LP
|
||||
.UL getw(ioptr)\ FILE\ *ioptr;
|
||||
.nr PD 0
|
||||
.IP
|
||||
.br
|
||||
returns the next word from the input stream named by
|
||||
.UL ioptr .
|
||||
.UL EOF
|
||||
is returned on end-of-file or error,
|
||||
but since this a perfectly good
|
||||
integer
|
||||
.UL feof
|
||||
and
|
||||
.UL ferror
|
||||
should be used.
|
||||
A ``word'' is 16 bits on the
|
||||
.UC PDP-11.
|
||||
.nr PD .4v
|
||||
.LP
|
||||
.UL putw(w,\ ioptr)\ FILE\ *ioptr;
|
||||
.nr PD 0
|
||||
.IP
|
||||
.br
|
||||
writes the integer
|
||||
.UL w
|
||||
on the named output stream.
|
||||
.nr PD .4v
|
||||
.LP
|
||||
.UL setbuf(ioptr,\ buf)\ FILE\ *ioptr;\ char\ *buf;
|
||||
.nr PD 0
|
||||
.IP
|
||||
.br
|
||||
.UL setbuf
|
||||
may be used after a stream has been opened
|
||||
but before I/O has started.
|
||||
If
|
||||
.UL buf
|
||||
is
|
||||
.UL NULL ,
|
||||
the stream will be unbuffered.
|
||||
Otherwise the buffer supplied will be used.
|
||||
It must be a character array of sufficient size:
|
||||
.P1
|
||||
char buf[BUFSIZ];
|
||||
.P2
|
||||
.nr PD .4v
|
||||
.LP
|
||||
.UL fileno(ioptr)\ FILE\ *ioptr;
|
||||
.nr PD 0
|
||||
.IP
|
||||
.br
|
||||
returns the integer file descriptor associated with the file.
|
||||
.nr PD .4v
|
||||
.LP
|
||||
.UL fseek(ioptr,\ offset,\ ptrname)\ FILE\ *ioptr;\ long\ offset;
|
||||
.nr PD 0
|
||||
.IP
|
||||
.br
|
||||
The location of the next byte in the stream
|
||||
named by
|
||||
.UL ioptr
|
||||
is adjusted.
|
||||
.UL offset
|
||||
is a long integer.
|
||||
If
|
||||
.UL ptrname
|
||||
is 0, the offset is measured from the beginning of the file;
|
||||
if
|
||||
.UL ptrname
|
||||
is 1, the offset is measured from the current read or
|
||||
write pointer;
|
||||
if
|
||||
.UL ptrname
|
||||
is 2, the offset is measured from the end of the file.
|
||||
The routine accounts properly for any buffering.
|
||||
(When this routine is used on
|
||||
.UC UNIX \& non-
|
||||
systems,
|
||||
the offset must be a value returned from
|
||||
.UL ftell
|
||||
and the ptrname must be 0).
|
||||
.ne 3
|
||||
.nr PD .4v
|
||||
.LP
|
||||
.UL long\ ftell(ioptr)\ FILE\ *ioptr;
|
||||
.nr PD 0
|
||||
.IP
|
||||
.br
|
||||
The byte offset, measured from the beginning of the file,
|
||||
associated with the named stream is returned.
|
||||
Any buffering is properly accounted for.
|
||||
(On
|
||||
.UC UNIX \& non-
|
||||
systems the value of this call is useful only
|
||||
for handing to
|
||||
.UL fseek ,
|
||||
so as to position the file to the same place it was when
|
||||
.UL ftell
|
||||
was called.)
|
||||
.nr PD .4v
|
||||
.LP
|
||||
.UL getpw(uid,\ buf)\ char\ *buf;
|
||||
.nr PD 0
|
||||
.IP
|
||||
.br
|
||||
The password file is searched for the given integer user ID.
|
||||
If an appropriate line is found, it is copied into
|
||||
the character array
|
||||
.UL buf ,
|
||||
and 0 is returned.
|
||||
If no line is found corresponding to the user ID
|
||||
then 1 is returned.
|
||||
.nr PD .4v
|
||||
.LP
|
||||
.UL char\ *malloc(num);
|
||||
.nr PD 0
|
||||
.IP
|
||||
.br
|
||||
allocates
|
||||
.UL num
|
||||
bytes.
|
||||
The pointer returned is sufficiently well aligned to be usable for any purpose.
|
||||
.UL NULL
|
||||
is returned if no space is available.
|
||||
.nr PD .4v
|
||||
.LP
|
||||
.UL char\ *calloc(num,\ size);
|
||||
.nr PD 0
|
||||
.IP
|
||||
.br
|
||||
allocates space for
|
||||
.UL num
|
||||
items each of size
|
||||
.UL size .
|
||||
The space is guaranteed to be set to 0 and the pointer is
|
||||
sufficiently well aligned to be usable for any purpose.
|
||||
.UL NULL
|
||||
is returned if no space is available .
|
||||
.nr PD .4v
|
||||
.LP
|
||||
.UL cfree(ptr)\ char\ *ptr;
|
||||
.nr PD 0
|
||||
.IP
|
||||
.br
|
||||
Space is returned to the pool used by
|
||||
.UL calloc .
|
||||
Disorder can be expected if the pointer was not obtained
|
||||
from
|
||||
.UL calloc .
|
||||
.nr PD .4v
|
||||
.LP
|
||||
The following are macros whose definitions may be obtained by including
|
||||
.UL <ctype.h> .
|
||||
.nr PD .4v
|
||||
.LP
|
||||
.UL isalpha(c)
|
||||
returns non-zero if the argument is alphabetic.
|
||||
.nr PD .4v
|
||||
.LP
|
||||
.UL isupper(c)
|
||||
returns non-zero if the argument is upper-case alphabetic.
|
||||
.nr PD .4v
|
||||
.LP
|
||||
.UL islower(c)
|
||||
returns non-zero if the argument is lower-case alphabetic.
|
||||
.nr PD .4v
|
||||
.LP
|
||||
.UL isdigit(c)
|
||||
returns non-zero if the argument is a digit.
|
||||
.nr PD .4v
|
||||
.LP
|
||||
.UL isspace(c)
|
||||
returns non-zero if the argument is a spacing character:
|
||||
tab, newline, carriage return, vertical tab,
|
||||
form feed, space.
|
||||
.nr PD .4v
|
||||
.LP
|
||||
.UL ispunct(c)
|
||||
returns non-zero if the argument is
|
||||
any punctuation character, i.e., not a space, letter,
|
||||
digit or control character.
|
||||
.nr PD .4v
|
||||
.LP
|
||||
.UL isalnum(c)
|
||||
returns non-zero if the argument is a letter or a digit.
|
||||
.nr PD .4v
|
||||
.LP
|
||||
.UL isprint(c)
|
||||
returns non-zero if the argument is printable \(em
|
||||
a letter, digit, or punctuation character.
|
||||
.nr PD .4v
|
||||
.LP
|
||||
.UL iscntrl(c)
|
||||
returns non-zero if the argument is a control character.
|
||||
.nr PD .4v
|
||||
.LP
|
||||
.UL isascii(c)
|
||||
returns non-zero if the argument is an ascii character, i.e., less than octal 0200.
|
||||
.nr PD .4v
|
||||
.LP
|
||||
.UL toupper(c)
|
||||
returns the upper-case character corresponding to the lower-case
|
||||
letter
|
||||
.UL c.
|
||||
.nr PD .4v
|
||||
.LP
|
||||
.UL tolower(c)
|
||||
returns the lower-case character corresponding to the upper-case
|
||||
letter
|
||||
.UL c .
|
Loading…
Reference in New Issue
Block a user