From c11e094d96120a2e0e726ed9705ae0ec08db49b6 Mon Sep 17 00:00:00 2001 From: Greg Lehey Date: Sun, 19 May 2002 06:11:50 +0000 Subject: [PATCH] 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 --- share/doc/psd/04.uprog/Makefile | 11 + share/doc/psd/04.uprog/p.mac | 39 ++ share/doc/psd/04.uprog/p0 | 49 +++ share/doc/psd/04.uprog/p1 | 55 +++ share/doc/psd/04.uprog/p2 | 242 ++++++++++++ share/doc/psd/04.uprog/p3 | 436 +++++++++++++++++++++ share/doc/psd/04.uprog/p4 | 567 ++++++++++++++++++++++++++++ share/doc/psd/04.uprog/p5 | 544 +++++++++++++++++++++++++++ share/doc/psd/04.uprog/p6 | 328 ++++++++++++++++ share/doc/psd/04.uprog/p8 | 29 ++ share/doc/psd/04.uprog/p9 | 647 ++++++++++++++++++++++++++++++++ 11 files changed, 2947 insertions(+) create mode 100644 share/doc/psd/04.uprog/Makefile create mode 100644 share/doc/psd/04.uprog/p.mac create mode 100644 share/doc/psd/04.uprog/p0 create mode 100644 share/doc/psd/04.uprog/p1 create mode 100644 share/doc/psd/04.uprog/p2 create mode 100644 share/doc/psd/04.uprog/p3 create mode 100644 share/doc/psd/04.uprog/p4 create mode 100644 share/doc/psd/04.uprog/p5 create mode 100644 share/doc/psd/04.uprog/p6 create mode 100644 share/doc/psd/04.uprog/p8 create mode 100644 share/doc/psd/04.uprog/p9 diff --git a/share/doc/psd/04.uprog/Makefile b/share/doc/psd/04.uprog/Makefile new file mode 100644 index 000000000000..c8b6abc30c47 --- /dev/null +++ b/share/doc/psd/04.uprog/Makefile @@ -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 diff --git a/share/doc/psd/04.uprog/p.mac b/share/doc/psd/04.uprog/p.mac new file mode 100644 index 000000000000..32d9030a26be --- /dev/null +++ b/share/doc/psd/04.uprog/p.mac @@ -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 +.. diff --git a/share/doc/psd/04.uprog/p0 b/share/doc/psd/04.uprog/p0 new file mode 100644 index 000000000000..4a59311c9b3d --- /dev/null +++ b/share/doc/psd/04.uprog/p0 @@ -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 diff --git a/share/doc/psd/04.uprog/p1 b/share/doc/psd/04.uprog/p1 new file mode 100644 index 000000000000..32efd316e595 --- /dev/null +++ b/share/doc/psd/04.uprog/p1 @@ -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]. diff --git a/share/doc/psd/04.uprog/p2 b/share/doc/psd/04.uprog/p2 new file mode 100644 index 000000000000..8f4ff04f8fee --- /dev/null +++ b/share/doc/psd/04.uprog/p2 @@ -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 : +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 + +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 +.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. diff --git a/share/doc/psd/04.uprog/p3 b/share/doc/psd/04.uprog/p3 new file mode 100644 index 000000000000..9e258bb10845 --- /dev/null +++ b/share/doc/psd/04.uprog/p3 @@ -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 + +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. diff --git a/share/doc/psd/04.uprog/p4 b/share/doc/psd/04.uprog/p4 new file mode 100644 index 000000000000..baddb5290d32 --- /dev/null +++ b/share/doc/psd/04.uprog/p4 @@ -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 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. diff --git a/share/doc/psd/04.uprog/p5 b/share/doc/psd/04.uprog/p5 new file mode 100644 index 000000000000..4987e112f8a2 --- /dev/null +++ b/share/doc/psd/04.uprog/p5 @@ -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 + +#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 + +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. diff --git a/share/doc/psd/04.uprog/p6 b/share/doc/psd/04.uprog/p6 new file mode 100644 index 000000000000..8d93a3681379 --- /dev/null +++ b/share/doc/psd/04.uprog/p6 @@ -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(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 + +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 +#include +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 + +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 diff --git a/share/doc/psd/04.uprog/p8 b/share/doc/psd/04.uprog/p8 new file mode 100644 index 000000000000..c38d79bc928d --- /dev/null +++ b/share/doc/psd/04.uprog/p8 @@ -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. diff --git a/share/doc/psd/04.uprog/p9 b/share/doc/psd/04.uprog/p9 new file mode 100644 index 000000000000..fa534df9c916 --- /dev/null +++ b/share/doc/psd/04.uprog/p9 @@ -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 +.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 . +.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 .