summary refs log tree commit diff
path: root/manual
diff options
context:
space:
mode:
authorMelissa Weisshaus <melissa@gnu.org>1992-03-20 23:49:29 +0000
committerMelissa Weisshaus <melissa@gnu.org>1992-03-20 23:49:29 +0000
commit47e5c515a17caa87a788c62eac54bbb806442618 (patch)
tree4f6b95322fc33846528e7a29aa517aae9a7763fb /manual
parentd5b56d2d8d86acf0864d2e693719159dee878ab6 (diff)
downloadglibc-47e5c515a17caa87a788c62eac54bbb806442618.tar.gz
glibc-47e5c515a17caa87a788c62eac54bbb806442618.tar.xz
glibc-47e5c515a17caa87a788c62eac54bbb806442618.zip
Initial revision
Diffstat (limited to 'manual')
-rw-r--r--manual/arith.texi464
-rw-r--r--manual/users.texi1016
2 files changed, 1480 insertions, 0 deletions
diff --git a/manual/arith.texi b/manual/arith.texi
new file mode 100644
index 0000000000..a0527f05e5
--- /dev/null
+++ b/manual/arith.texi
@@ -0,0 +1,464 @@
+@node Arithmetic
+@chapter Low-Level Arithmetic Functions
+
+This chapter contains information about functions for doing basic
+arithmetic operations, such as splitting a float into its integer and
+fractional parts.  These functions are declared in the header file
+@file{math.h}.
+
+@menu
+* Normalization Functions::		Hacks for radix-2 representations.
+* Rounding and Remainder Functions::	Determinining the integer and
+					 fractional parts of a float.
+* Integer Division::			Functions for performing integer
+					 division.
+* Parsing of Numbers::			Functions for ``reading'' numbers
+					 from strings.
+* Predicates on Floats::		Some miscellaneous test functions.
+@end menu
+
+@node Normalization Functions
+@section Normalization Functions
+@cindex normalization functions (floating-point)
+
+The functions described in this section are primarily provided as a way
+to efficiently perform certain low-level manipulations on floating point
+numbers that are represented internally using a binary radix;
+see @ref{Floating-Point Representation}.  These functions are required to
+have equivalent behavior even if the representation does not use a radix
+of 2, but of course they are unlikely to be particularly efficient in
+those cases.
+
+@comment math.h
+@comment GNU
+@deftypefun double copysign (double @var{value}, double @var{sign})
+The @code{copysign} function returns a value whose absolute value is the
+same as that of @var{value}, and whose sign matches that of @var{sign}.
+@end deftypefun
+
+@comment math.h
+@comment ANSI
+@deftypefun double frexp (double @var{value}, int *@var{exponent})
+The @code{frexp} function is used to normalize the number @var{value}.
+
+If the argument @var{value} is not zero, the return value is a
+floating-point number with magnitude in the range 1/2 (inclusive) to 1
+(exclusive).  The corresponding exponent is stored in the location
+pointed at by @var{exponent}; the return value multiplied by 2 raised to
+this exponent would equal the original number @var{value}.
+
+If @var{value} is zero, then both parts of the result are zero.
+@end deftypefun
+
+@comment math.h
+@comment ANSI
+@deftypefun double ldexp (double @var{value}, int @var{exponent})
+This function returns the result of multiplying the floating-point
+number @var{value} by 2 raised to the power @var{exponent}.  (It can
+be used to reassemble floating-point numbers that were taken apart
+by @code{frexp}.)
+@end deftypefun
+
+@c ??? Where does this come from?
+@comment math.h
+@comment GNU
+@deftypefun double scalb (double @var{value}, int @var{exponent})
+The @code{scalb} function does the same thing as @code{ldexp}.
+@end deftypefun
+
+@c ??? Where does this come from?
+@comment math.h
+@comment GNU
+@deftypefun double logb (double @var{x})
+This function returns the integer part of the base-2 logarithm of
+@var{x}, an integer value represented in type @code{double}.  This is
+the highest integer power of @code{2} contained in @var{x}.
+
+When @code{2} raised to this power is divided into @var{x}, it gives a
+quotient between @code{1} (inclusive) and @code{2} (exclusive).
+
+@strong{Incomplete:}  What happens if @var{x} is zero?
+@end deftypefun
+
+
+@node Rounding and Remainder Functions
+@section Rounding and Remainder Functions
+@cindex rounding functions
+@cindex remainder functions
+@cindex converting floats to integers
+
+The functions listed here perform operations such as rounding,
+truncation, and remainder in division of floating point numbers.  Some
+of these functions convert floating point numbers to integer values.
+
+You can also convert floating-point numbers to integers simply by
+casting them to @code{int}.  This discards the fractional part,
+effectively rounding towards zero.  However, this only works if the
+result can actually be represented as an @code{int}---for very large
+numbers, this is impossible.  The functions listed here return the
+result as a @code{double} instead to get around this problem.
+
+@comment math.h
+@comment ANSI
+@deftypefun double ceil (double @var{x})
+The @code{ceil} function rounds @var{x} upwards to the nearest integer,
+returning that value as a @code{double}.
+@end deftypefun
+
+@comment math.h
+@comment ANSI
+@deftypefun double floor (double @var{x})
+The @code{ceil} function rounds @var{x} downwards to the nearest
+integer, returning that value as a @code{double}.
+@end deftypefun
+
+@comment math.h
+@comment GNU
+@deftypefun double rint (double @var{x})
+This function returns the integer nearest @var{x} according to the
+current rounding mode.  @xref{Floating-Point Parameters}, for information
+about the @code{FLT_ROUNDS} macro.
+@end deftypefun
+
+@comment math.h
+@comment ANSI
+@deftypefun double modf (double @var{value}, double *@var{integer_part})
+This function breaks the argument @var{value} into an integer part and a
+fractional part (between @code{-1} and @code{1}, exclusive).  The
+integer part is stored at the location pointed at by @var{integer_part},
+and the fractional part is returned.  Their sum equals @var{value}.
+Each of the parts has the same sign as @var{value}, so the rounding of
+the integer part is towards zero.
+@end deftypefun
+
+
+@comment math.h
+@comment ANSI
+@deftypefun double fmod (double @var{numerator}, double @var{denominator})
+This function computes the remainder of dividing @var{numerator} by
+@var{denominator}.  Specifically, the return value is
+@code{@var{numerator} - @var{n} * @var{denominator}}, where @var{n} is
+the quotient of @var{numerator} by @var{denominator}, rounded down to
+the next lower integer.
+
+The result has the same sign as the @var{numerator} and has magnitude
+less than the magnitude of the @var{denominator}.  (Recall that the
+built-in @samp{%} operator isn't defined on floating-point values.)
+
+The following @code{errno} error conditions are defined for this function:
+
+@table @code
+@item EDOM
+The @var{denominator} is zero.
+@end table
+@end deftypefun
+
+@comment math.h
+@comment GNU
+@deftypefun double drem (double @var{numerator}, double @var{denominator})
+This function returns the remainder from dividing @var{numerator} by
+@var{denominator}.  Specifically, the return value is @code{@var{numerator}
+- @var{n} * @var{denominator}}, where @var{n} is the integer closest to
+the exact quotient of @var{numerator} and @var{denominator}.  The absolute
+value of the result is less than or equal to one half the absolute value
+of the @var{denominator}.
+
+The following @code{errno} error conditions are defined for this function:
+
+@table @code
+@item EDOM
+The @var{denominator} is zero.
+@end table
+@end deftypefun
+
+
+@node Integer Division
+@section Integer Division
+@cindex integer division functions
+
+This section describes functions for performing integer division.  These
+functions are redundant in the GNU C library, since in GNU C the @samp{/}
+operator always rounds towards zero.  But in other C implementations,
+@samp{/} may round differently with negative arguments.  @code{div} and
+@code{ldiv} are useful because they specify how to round the quotient.
+
+These functions are specified to return a result @var{r} such that
+@code{@var{r}.quot*@var{denominator} + @var{r}.rem} equals
+@var{numerator}.
+
+To use these facilities, you should include the header file
+@file{stdlib.h} in your program.
+@pindex stdlib.h
+
+@comment stdlib.h
+@comment ANSI
+@deftp {Data Type} div_t
+This is a structure type used to hold the result returned by the @code{div}
+function.  It has the following members:
+
+@table @code
+@item int quot
+The quotient from the division.
+
+@item int rem
+The remainder from the division.
+@end table
+@end deftp
+
+@comment stdlib.h
+@comment ANSI
+@deftypefun div_t div (int @var{numerator}, int @var{denominator})
+This function @code{div} computes the quotient and remainder from
+the division of @var{numerator} by @var{denominator}, returning the
+result in a structure of type @code{div_t}.
+
+If the result cannot be represented (as in a division by zero), the
+behavior is undefined.
+@end deftypefun
+
+
+@comment stdlib.h
+@comment ANSI
+@deftp {Data Type} ldiv_t
+This is a structure type used to hold the result returned by the @code{ldiv}
+function.  It has the following members:
+
+@table @code
+@item long int quot
+The quotient from the division.
+
+@item long int rem
+The remainder from the division.
+@end table
+
+(This is identical to the type @code{div_t} except that the components
+are of type @code{long int} rather than @code{int}.)
+@end deftp
+
+@comment stdlib.h
+@comment ANSI
+@deftypefun ldiv_t ldiv (long int @var{numerator}, long int @var{denominator})
+The @code{ldiv} function is similar to @code{div}, except that the
+arguments are of type @code{long int} and the result is returned as a
+structure of type @code{ldiv}.
+@end deftypefun
+
+
+@node Parsing of Numbers
+@section Parsing of Numbers
+@cindex parsing numbers (in formatted input)
+@cindex converting strings to numbers
+@cindex number syntax, parsing
+@cindex syntax, for reading numbers
+
+This section describes functions for ``reading'' integer and
+floating-point numbers from a string.  In many cases, it is more
+appropriate to use @code{sscanf} or one of the related functions;
+see @ref{Formatted Input}.  The syntax recognized by the formatted input
+functions for the numeric conversions is exactly the same as the syntax
+recognized by the functions described in this section.
+
+These functions are declared in @file{stdlib.h}.
+@pindex stdlib.h
+
+@menu
+* Parsing of Integers::		Functions for conversion of integer values.
+* Parsing of Floats::		Functions for conversion of floating-point
+				 values.
+@end menu
+
+@node Parsing of Integers
+@subsection Parsing of Integers
+
+@comment stdlib.h
+@comment ANSI
+@deftypefun {long int} strtol (const char *@var{string}, char **@var{tailptr}, int @var{base})
+The @code{strtol} (``string-to-long'') function converts the initial
+part of @var{string} to a signed integer, which is returned as a value
+of type @code{long int}.  
+
+This function attempts to decompose @var{string} as follows:
+
+@itemize @bullet
+@item 
+A (possibly empty) sequence of whitespace characters.  Which characters
+are whitespace is determined by the @code{isspace} function
+(@pxref{Classification of Characters}).  These are discarded.
+
+@item 
+An optional plus or minus sign (@samp{+} or @samp{-}).
+
+@item 
+A nonempty sequence of digits in the radix specified by @var{base}.  If
+@var{base} is zero, decimal radix is assumed unless the series of digits
+begins with @samp{0} (specifying octal radix), or @samp{0x} or @samp{0X}
+(specifying hexadecimal radix); in other words, the same syntax that is
+used for integer constants in the C language is recognized.  Otherwise
+@var{base} must have a value between @code{2} and @code{35}.  If
+@var{base} is @code{16}, the digits may optionally be preceeded by
+@samp{0x} or @samp{0X}.
+
+@item 
+Any remaining characters in the string.  If @var{tailptr} is not a null
+pointer, a pointer to this tail of the string is stored in
+@code{*@var{tailptr}}.
+@end itemize
+
+If the string is empty, contains only whitespace, or does not contain an
+initial substring that has the expected syntax for an integer in the
+specified @var{base}, no conversion is performed.  In this case,
+@code{strtol} returns a value of zero and the value returned in
+@code{*@var{tailptr}} is the value of @var{string}.
+
+In a locale other than the standard @code{"C"} locale, this function
+may recognize additional implementation-dependent syntax.
+
+If the string has valid syntax for an integer but the value is not
+representable because of overflow, @code{strtol} returns either
+@code{LONG_MAX} or @code{LONG_MIN} (@pxref{Integer Representation
+Limits}), as appropriate for the sign of the value.
+
+The following @code{errno} error conditions are defined for this
+function:
+
+@table @code
+@item ERANGE
+An overflow condition was detected.
+@end table
+@end deftypefun
+
+@comment stdlib.h
+@comment ANSI
+@deftypefun {unsigned long int} strtoul (const char *@var{string}, char **@var{tailptr}, int @var{base})
+The @code{strtoul} (``string-to-unsigned-long'') function is similar to
+@code{strtol} except that it returns its value as an object of type
+@code{unsigned long int}.  The value returned in case of overflow is
+@code{ULONG_MAX} (@pxref{Integer Representation Limits}).
+@end deftypefun
+
+@comment stdlib.h
+@comment ANSI
+@deftypefun {long int} atol (const char *@var{string})
+This function is similar to the @code{strtol} function with a @var{base}
+argument of @code{10}, except that it need not detect overflow errors.
+The @code{atol} function is provided mostly for compatibility with
+existing code; using @code{strtol} is more robust.
+@end deftypefun
+
+@comment stdlib.h
+@comment ANSI
+@deftypefun int atoi (const char *@var{string})
+This function is similar to the @code{atol} function, except that
+returns its value as an @code{int} rather than @code{long int}.  The
+@code{atoi} function is also considered obsolete; use @code{strtol}
+instead.
+@end deftypefun
+
+
+@node Parsing of Floats
+@subsection Parsing of Floats
+
+@comment stdlib.h
+@comment ANSI
+@deftypefun double strtod (const char *@var{string}, char **@var{tailptr})
+The @code{strtod} (``string-to-double'') function converts the initial
+part of @var{string} to a floating-point number, which is returned as a
+value of type @code{double}.  
+
+This function attempts to decompose @var{string} as follows:
+
+@itemize @bullet
+@item 
+A (possibly empty) sequence of whitespace characters.  Which characters
+are whitespace is determined by the @code{isspace} function
+(@pxref{Classification of Characters}).  These are discarded.
+
+@item
+An optional plus or minus sign (@samp{+} or @samp{-}).
+
+@item
+A nonempty sequence of digits optionally containing a decimal-point
+character (@samp{.}).
+
+@item
+An optional exponent part, consisting of a character @samp{e} or
+@samp{E}, an optional sign, and a sequence of digits.
+
+@item
+Any remaining characters in the string.  If @var{tailptr} is not a null
+pointer, a pointer to this tail of the string is stored in
+@code{*@var{tailptr}}.
+@end itemize
+
+If the string is empty, contains only whitespace, or does not contain an
+initial substring that has the expected syntax for a floating-point
+number, no conversion is performed.  In this case, @code{strtod} returns
+a value of zero and the value returned in @code{*@var{tailptr}} is the
+value of @var{string}.
+
+In a locale other than the standard @code{"C"} locale, this function may
+recognize additional locale-dependent syntax.
+
+If the string has valid syntax for a floating-point number but the value
+is not representable because of overflow, @code{strtod} returns either
+positive or negative @code{HUGE_VAL} (@pxref{Mathematics}), depending on
+the sign of the value.  Similarly, if the value is not representable
+because of underflow, @code{strtod} returns zero.
+
+The following @code{errno} error conditions are defined for this
+function:
+
+@table @code
+@item ERANGE
+An overflow or underflow condition was detected.
+@end table
+@end deftypefun
+
+@comment stdlib.h
+@comment ANSI
+@deftypefun double atof (const char *@var{string})
+This function is similar to the @code{strtod} function, except that it
+need not detect overflow and underflow errors.  The @code{atof} function
+is provided mostly for compatibility with existing code; using
+@code{strtod} is more robust.
+@end deftypefun
+
+@node Predicates on Floats
+@section Predicates on Floats
+@cindex predicates on floats
+
+This section describes some miscellaneous test functions on doubles.
+Prototypes for these functions appear in @file{math.h}.
+@pindex math.h
+
+@comment math.h
+@comment GNU
+@deftypefun int isinf (double @var{x})
+This function returns @code{-1} if @var{x} represents negative infinity,
+@code{1} if @var{x} represents positive infinity, and @code{0} otherwise.
+@end deftypefun
+
+@comment math.h
+@comment GNU
+@deftypefun int isnan (double @var{x})
+This function returns a nonzero value if @var{x} is a ``not a number''
+value, and zero otherwise.
+@end deftypefun
+
+@comment math.h
+@comment GNU
+@deftypefun int finite (double @var{x})
+This function returns a nonzero value if @var{x} is finite or a ``not a
+number'' value, and zero otherwise.
+@end deftypefun
+
+@comment math.h
+@comment GNU
+@deftypefun double infnan (int @var{error})
+@strong{Incomplete:}  I don't understand what this function does.
+@end deftypefun
+
+@strong{Portability Note:} The functions listed in this section are GNU
+extensions.
+
+
diff --git a/manual/users.texi b/manual/users.texi
new file mode 100644
index 0000000000..92aa86d07d
--- /dev/null
+++ b/manual/users.texi
@@ -0,0 +1,1016 @@
+@node Users and Groups
+@chapter Users and Groups
+@cindex persona
+
+Every user who can log in on the system is identified by a unique number
+called the @dfn{user ID}.  Each process has an effective user ID which
+says which user's access permissions it has.
+
+Users are classified into @dfn{groups} for access control purposes.  Each
+process has one or more @dfn{group ID values} which say which groups the
+process can use for access to files.
+
+The effective user and group IDs of a process collectively form its
+@dfn{persona}.  This determines which files the process can access.
+Normally, a process inherits its persona from the parent process, but
+under special circumstances a process can change its persona and thus
+change its access permissions.
+
+Each file in the system also has a user ID and a group ID.  Access
+control works by comparing the user and group IDs of the file with those
+of the running process.
+
+The system keeps a database of all the registered users, and another
+database of all the defined groups.  There are library functions you
+can use to examine these databases.
+
+@menu
+* User and Group IDs::  Each user has a unique numeric ID; likewise for groups.
+* Process Persona::	The user IDs and group IDs of a process.
+* Why Change Persona::	Why a program might need to change
+			 its user and/or group IDs.
+* How Change Persona::	Restrictions on changing the user and group IDs.
+* Reading Persona::	How to examine the process's user and group IDs.
+* Setting User ID::
+* Setting Groups::
+* Enable/Disable Setuid:: 
+* Setuid Example::	A program designed to use setuid.
+
+* Tips for Setuid::
+
+* Who Logged In::	Getting the name of the user who logged in,
+			 or of the real user ID of the current process.
+
+* User Database::	Functions and data structures for
+                         accessing the user database.
+* Group Database::	Functions and data structures for
+                         accessing the group database.
+* Database Example::	Example program showing use of database
+			 inquiry functions.
+@end menu
+
+@node User and Group IDs
+@section User and Group IDs
+
+@cindex login name
+@cindex user name
+@cindex user ID
+Each user account on a computer system is identified by a @dfn{user
+name} (or @dfn{login name}) and @dfn{user ID}.  Normally, each user name
+has a unique user ID, but it is possible for several login names to have
+the same user ID.  The user names and corresponding user IDs are stored
+in a data base which you can access as described in @ref{User Database}.
+
+@cindex group name
+@cindex group ID
+Users are classified in @dfn{groups}.  Each user name also belongs to
+one or more groups, and has one @dfn{default group}.  Users who are
+members of the same group can share resources (such as files) that are
+not accessible to users who are not a member of that group.  Each group
+has a @dfn{group name} and @dfn{group ID}.  @xref{Group Database},
+for how to find information about a group ID or group name.
+
+@node Process Persona
+@section The Persona of a Process
+
+@cindex effective user ID
+@cindex effective group ID
+At any time, each process has a single user ID and a group ID which
+determine the privileges of the process.  These are collectively called
+the @dfn{persona} of the process, because they determine ``who it is''
+for purposes of access control.  These IDs are also called the
+@dfn{effective user ID} and @dfn{effective group ID} of the process.
+
+Your login shell starts out with a persona which consists of your user
+ID and your default group ID.  In normal circumstances. all your other
+processes inherit these values.
+
+@cindex real user ID
+@cindex real group ID
+A process also has a @dfn{real user ID} which identifies the user who
+created the process, and a @dfn{real group ID} which identifies that
+user's default group.  These values do not play a role in access
+control, so we do not consider them part of the persona.  But they are
+also important.
+
+Both the real and effective user ID can be changed during the lifetime
+of a process.  @xref{Changing Persona}.
+
+@cindex supplementary group IDs
+In addition, a user can belong to multiple groups, so the persona
+includes have @dfn{supplementary group IDs} that also contribute to
+access permission.
+
+For details on how a process's effective user IDs and group IDs affect
+its permission to access files, see @ref{Access Permission}.
+
+The user ID of a process also controls permissions for sending signals
+using the @code{kill} function.  @xref{Signaling Another Process}.
+
+@node Why Change Persona
+@section Why Change the Persona of a Process?
+
+The most obvious situation where it is necessary for a process to change
+its user and/or group IDs is the @code{login} program.  When
+@code{login} starts running, its user ID is @code{root}.  Its job is to
+start a shell whose user and group IDs are those of the user who is
+logging in.  In fact, @code{login} must set the real user and group IDs
+as well as its persona.  But this is a special case.
+
+The more common case of changing persona is when an ordinary user
+programs needs access to a resource that wouldn't ordinarily be
+accessible to the user actually running it.
+
+For example, you may have a file that is controlled by your program but
+that shouldn't be read or modified directly by other users, either
+because it implements some kind of locking protocol, or because you want
+to preserve the integrity or privacy of the information it contains.
+This kind of restricted access can be implemented by having the program
+change its effective user or group ID to match that of the resource.
+
+Thus, imagine a game program that saves scores in a file.  The game
+program itself needs to be able to update this file no matter who is
+running it, but if users can write the file without going through the
+game, they can give themselves any scores they like.  Some people
+consider this undesirable, or even reprehensible.  It can be prevented
+by creating a new user ID and login name (say, @samp{games}) to own the
+scores file, and make the file writable only by this user.  Then, when
+the game program wants to update this file, it can change its effective
+user ID to be that for @samp{games}.  In effect, the program must
+adopt the persona of @samp{games} so it can write the scores file.
+
+@node How Change Persona
+@section How an Application can Change Persona
+@cindex @code{setuid} programs
+
+The ability to change the persona of a process can be a source of
+unintentional privacy violations, or even intentional abuse.  Because of
+the potential for problems, changing persona is restricted to special
+circumstances.
+
+You can't just arbitrarily set your user ID or group ID to anything you
+want; only privileged users can do that.  Instead, the normal way for a
+program to change its persona is that it has been set up in advance to
+change to a particular user or group.  This is the function of the suid
+and sgid bits of a file's access mode.
+
+When the suid bit of an executable file is set, executing that file
+automatically changes the effective user ID to the user that owns the
+file.  Likewise, executing a file whose sgid bit is set changes the
+effective group ID to the group of the file.  @xref{Executing a File}.
+Creating a file that changes to a particular user or group ID thus
+requires full access to that user or group ID.
+
+@xref{File Attributes}, for a more general discussion of file modes and
+accessibility.
+
+A process can always change its effective user (or group) ID back to its
+real ID.  Programs do this so as to turn off their special privileges
+when they are not needed, which makes for more robustness.
+
+@node Reading Persona
+@section Reading the Persona of a Process
+
+Here are detailed descriptions of the functions for reading the user and
+group IDs of a process, both real and effective.  To use these
+facilities, you must include the header files @file{sys/types.h} and
+@file{unistd.h}.
+@pindex unistd.h
+@pindex sys/types.h
+
+@comment sys/types.h
+@comment POSIX.1
+@deftp {Data Type} uid_t
+This is an integer data type used to represent user IDs.  In the GNU
+library, this is an alias for @code{unsigned short int}.
+@end deftp
+
+@comment sys/types.h
+@comment POSIX.1
+@deftp {Data Type} gid_t
+This is an integer data type used to represent group IDs.  In the GNU
+library, this is an alias for @code{unsigned short int}.
+@end deftp
+
+@comment unistd.h
+@comment POSIX.1
+@deftypefun uid_t getuid ()
+The @code{getuid} function returns the real user ID of the process.
+@end deftypefun
+
+@comment unistd.h
+@comment POSIX.1
+@deftypefun gid_t getgid ()
+The @code{getgid} function returns the real group ID of the process.
+@end deftypefun
+
+@comment unistd.h
+@comment POSIX.1
+@deftypefun uid_t geteuid ()
+The @code{geteuid} function returns the effective user ID of the process.
+@end deftypefun
+
+@comment unistd.h
+@comment POSIX.1
+@deftypefun gid_t getegid ()
+The @code{getegid} function returns the effective group ID of the process.
+@end deftypefun
+
+@comment unistd.h
+@comment POSIX.1
+@deftypefun int getgroups (int @var{count}, gid_t *@var{groups})
+The @code{getgroups} function is used to inquire about the supplementary
+group IDs of the process.  Up to @var{count} of these group IDs are
+stored in the array @var{groups}; the return value from the function is
+the number of group IDs actually stored.  If @var{count} is smaller than
+the total number of supplementary group IDs, then @code{getgroups}
+returns a value of @code{-1} and @code{errno} is set to @code{EINVAL}.
+
+If @var{count} is zero, then @code{getgroups} just returns the total
+number of supplementary group IDs.
+
+Here's how to use @code{getgroups} to read all the supplementary group
+IDs:
+
+@example
+gid_t *
+read_all_groups ()
+@{
+  int ngroups = getgroups (0, 0);
+  gid_t *groups = (gid_t *) xmalloc (ngroups * sizeof (gid_t));
+  int val = getgroups (ngroups, groups);
+  if (val < 0)
+    return 0;
+  return groups;
+@}
+@end example
+@end deftypefun
+
+@node Setting User ID
+@section Setting the User ID
+
+This section describes the functions for altering the user ID
+of a process.  To use these facilities, you must include the header
+files @file{sys/types.h} and @file{unistd.h}.
+@pindex unistd.h
+@pindex sys/types.h
+
+@comment unistd.h
+@comment POSIX.1
+@deftypefun int setuid (@var{newuid})
+This function sets both the real and effective user ID of the process
+to @var{newuid}, provided that the process has appropriate privileges.
+
+If the process is not privileged, then @var{newuid} must either be equal
+to the real user ID or the saved user ID (but only if the system
+supports the @code{_POSIX_SAVED_IDS} feature).  In this case,
+@code{setuid} sets only the effective user ID and not the real user ID.
+
+The @code{setuid} function returns a value of @code{0} to indicate
+successful completion, and a value of @code{-1} to indicate an error.
+The following @code{errno} error conditions are defined for this
+function:
+
+@table @code
+@item EINVAL
+The value of the @var{newuid} argument is invalid.
+
+@item EPERM
+The process does not have the appropriate privileges; you do not
+have permission to change to the specified ID.  @xref{Controlling Process
+Privileges}.
+@end table
+@end deftypefun
+
+@comment unistd.h
+@comment BSD
+@deftypefun int setreuid (int @var{ruid}, int @var{euid})
+This function sets the real user ID of the process to @var{ruid} and
+the effective user ID to @var{euid}.
+
+The @code{setreuid} function is provided for compatibility with 4.2 BSD
+Unix, which does not support saved IDs.  You can use this function to
+swap the effective and real user IDs of the process.  (Privileged users
+can make other changes as well.)  If saved IDs are supported, you should
+use that feature instead of this function.
+
+The return value is @code{0} on success and @code{-1} on failure.
+The following @code{errno} error conditions are defined for this
+function:
+
+@table @code
+@item EPERM
+The process does not have the appropriate privileges; you do not
+have permission to change to the specified ID.  @xref{Controlling Process
+Privileges}.
+@end table
+@end deftypefun
+
+@node Setting Groups
+@section Setting the Group IDs
+
+@comment unistd.h
+@comment POSIX.1
+@deftypefun int setgid (@var{newgid})
+This function sets both the real and effective group ID of the process
+to @var{newgid}, provided that the process has appropriate privileges.
+
+If the process is not privileged, then @var{newgid} must either be equal
+to the real group ID or the saved group ID.  In this case, @code{setgid}
+sets only the effective group ID and not the real group ID.
+
+The return values and error conditions for @code{setgid} are the same
+as those for @code{setuid}.
+@end deftypefun
+
+
+@comment unistd.h
+@comment BSD
+@deftypefun int setregid (int @var{rgid}, int @var{egid})
+This function sets the real group ID of the process to @var{rgid} and
+the effective group ID to @var{egid}.
+
+The @code{setregid} function is provided for compatibility with 4.2 BSD
+Unix, which does not support saved IDs.  You can use this function to
+swap the effective and real group IDs of the process.  (Privileged users
+can make other changes.)  If saved IDs are supported, you should make use 
+of that feature instead of using this function.
+
+The return values and error conditions for @code{setregid} are the same
+as those for @code{setreuid}.
+@end deftypefun
+
+The GNU system also lets privileged processes change their supplementary 
+group IDs.  To use @code{setgroups} or @code{initgroups}, your programs
+should include the header file @file{grp.h}.
+@pindex grp.h
+
+@comment grp.h
+@comment BSD
+@deftypefun int setgroups (size_t @var{count}, gid_t *@var{groups})
+This function sets the process's supplementary group IDs.  It can only
+be called from privileged processes.  The @var{count} argument specifies
+the number of group IDs in the array @var{groups}.
+
+This function returns @code{0} if successful and @code{-1} on error.
+The following @code{errno} error conditions are defined for this
+function:
+
+@table @code
+@item EPERM
+The calling process is not privileged.
+@end table
+@end deftypefun
+
+@comment grp.h
+@comment BSD
+@deftypefun int initgroups (const char *@var{user}, gid_t @var{gid})
+The @code{initgroups} function effectively calls @code{setgroups} to
+set the process's supplementary group IDs to be the normal default for
+the user name @var{user}.  The group ID @var{gid} is also included.
+@end deftypefun
+
+@node Enable/Disable Setuid
+@section Turning Setuid Access On and Off
+
+You can use @code{setreuid} to swap the real and effective user IDs of
+the process, as follows:
+
+@example
+setreuid (geteuid (), getuid ());
+@end example
+
+@noindent
+This special case is always allowed---it cannot fail.  A @code{setuid}
+program can use this to turn its special access on and off.  For
+example, suppose a game program has just started, and its real user ID
+is @code{jdoe} while its effective user ID is @code{games}.  In this
+state, the game can write the scores file.  If it swaps the two uids,
+the real becomes @code{games} and the effective becomes @code{jdoe}; now
+the program has only @code{jdoe} to access.  Another swap brings
+@code{games} back to the effective user ID and restores access to the
+scores file.
+
+If the system supports the saved user ID feature, you can accomplish 
+the same job with @code{setuid}.  When the game program starts, its
+real user ID is @code{jdoe}, its effective user ID is @code{games}, and
+its saved user ID is also @code{games}.  The program should record both
+user ID values once at the beginning, like this:
+
+@example
+user_user_id = getuid ();
+game_user_id = geteuid ();
+@end example
+
+Then it can turn off game file access with 
+
+@example
+setuid (user_user_id);
+@end example
+
+@noindent
+and turn it on with 
+
+@example
+setuid (game_user_id);
+@end example
+
+@noindent
+Throughout the process, the real user ID remains @code{jdoe} and the 
+saved user ID remains @code{games}.
+
+@node Setuid Program Example
+@section Setuid Program Example
+
+Here's an example showing how to set up a program that changes its
+effective user ID.
+
+This is part of a game program called @code{caber-toss} that
+manipulates a file @file{scores} that should be writable only by the game
+program itself.  The program assumes that its executable
+file will be installed with the set-user-ID bit set and owned by the
+same user as the @file{scores} file.  Typically, a system
+administrator will set up an account like @samp{games} for this purpose.
+
+The executable file is given mode @code{4755}, so that doing an 
+@samp{ls -l} on it produces output like:
+
+@example
+-rwsr-xr-x   1 games    184422 Jul 30 15:17 caber-toss
+@end example
+
+@noindent
+The set-user-ID bit shows up in the file modes as the @samp{s}.
+
+The scores file is given mode @code{644}, and doing an @samp{ls -l} on
+it shows:
+
+@example
+-rw-r--r--  1 games           0 Jul 31 15:33 scores
+@end example
+
+Here are the parts of the program that show how to set up the changed
+user ID.  This program is conditionalized so that it makes use of the
+saved IDs feature if it is supported, and otherwise uses @code{setreuid}
+to swap the effective and real user IDs.
+
+@example
+#include <stdio.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <stdlib.h>
+
+
+/* @r{Save the effective and real UIDs.} */
+
+uid_t euid, ruid;
+
+
+/* @r{Restore the effective UID to its original value.} */
+
+void
+do_setuid ()
+@{
+  int status;
+
+#ifdef _POSIX_SAVED_IDS
+  status = setuid (euid);
+#else
+  status = setreuid (ruid, euid);
+#endif
+  if (status < 0) @{
+    fprintf (stderr, "Couldn't set uid.\n");
+    exit (status);
+    @}
+@}
+
+
+/* @r{Set the effective UID to the real UID.} */
+
+void
+undo_setuid ()
+@{
+  int status;
+
+#ifdef _POSIX_SAVED_IDS
+  status = setuid (ruid);
+#else
+  status = setreuid (euid, ruid);
+#endif
+  if (status < 0) @{
+    fprintf (stderr, "Couldn't set uid.\n");
+    exit (status);
+    @}
+@}
+
+
+/* @r{Main program.} */
+
+void
+main ()
+@{
+  /* @r{Save the real and effective user IDs.}  */
+  ruid = getuid ();
+  euid = geteuid ();
+  undo_setuid ();
+
+  /* @r{Do the game and record the score.}  */
+  @dots{}
+@}
+@end example
+
+Notice how the first thing the @code{main} function does is to set the
+effective user ID back to the real user ID.  This is so that any other
+file accesses that are performed while the user is playing the game use
+the real user ID for determining permissions.  Only when the program
+needs to open the scores file does it switch back to the original
+effective user ID, like this:
+
+@example
+/* @r{Record the score.} */
+
+int
+record_score (int score)
+@{
+  FILE *stream;
+  char *myname;
+
+  /* @r{Open the scores file.} */
+  do_setuid ();
+  stream = fopen (SCORES_FILE, "a");
+  undo_setuid ();
+
+  /* @r{Write the score to the file.} */
+  if (stream) @{
+    myname = cuserid (NULL);
+    if (score < 0)
+      fprintf (stream, "%10s: Couldn't lift the caber.\n", myname);
+    else
+      fprintf (stream, "%10s: %d feet.\n", myname, score);
+    fclose (stream);
+    return 0;
+    @}
+  else
+    return -1;
+@}
+@end example
+
+@node Tips for Setuid
+@section Tips for Writing Setuid Programs
+
+It is easy for setuid programs to give the user access that isn't 
+intended---in fact, if you want to avoid this, you need to be careful.
+Here are some guidelines for preventing unintended access and
+minimizing its consequences when it does occur:
+
+@itemize @bullet
+@item
+Don't have @code{setuid} programs with privileged user IDs such as
+@samp{root} unless it is absolutely necessary.  If the resource is
+specific to your particular program, it's better to define a new,
+nonprivileged user ID or group ID just to manage that resource.
+
+@item
+Be cautious about using the @code{system} and @code{exec} functions in
+combination with changing the effective user ID.  Don't let users of
+your program execute arbitrary programs under a changed user ID.
+Executing a shell is especially bad news.  Less obviously, the
+@code{execlp} and @code{execvp} functions are a potential risk (since
+the program they execute depends on the user's @code{PATH} environment
+variable).
+
+If you must @code{exec} another program under a changed ID, specify an
+absolute file name (@pxref{File Name Resolution}) for the executable,
+and make sure that the protections on that executable and @emph{all}
+containing directories are such that ordinary users cannot replace it
+with some other program.
+
+@item
+Only use the user ID controlling the resource in the part of the program
+that actually uses that resource.  When you're finished with it, restore
+the effective user ID back to the actual user's user ID.
+@xref{Enable/Disable Setuid}.
+
+@item
+If the @code{setuid} part of your program needs to access other files
+besides the controlled resource, it should verify that the real user
+would ordinarily have permission to access those files.  You can use the
+@code{access} function (@pxref{Access Permission}) to check this; it
+uses the real user and group IDs, rather than the effective IDs.
+@end itemize
+
+@node Who Logged In
+@section Identifying Who Logged In
+@cindex login name, determining
+@cindex user ID, determining
+
+You can use the functions listed in this section to determine the login
+name of the user who is running a process, and the name of the user who
+logged in the current session.  See also the function @code{getuid} and
+friends (@pxref{User and Group ID Functions}).
+
+The @code{getlogin} function is declared in @file{unistd.h}, while
+@code{cuserid} and @code{L_cuserid} are declared in @file{stdio.h}.
+@pindex stdio.h
+@pindex unistd.h
+
+@comment unistd.h
+@comment POSIX.1
+@deftypefun {char *} getlogin ()
+The @code{getlogin} function returns a pointer to string containing the
+name of the user logged in on the controlling terminal of the process,
+or a null pointer if this information cannot be determined.  The string
+is statically allocated and might be overwritten on subsequent calls to
+this function or to @code{cuserid}.
+@end deftypefun
+
+@comment stdio.h
+@comment POSIX.1
+@deftypefun {char *} cuserid (@var{string})
+The @code{cuserid} function returns a pointer to a string containing a
+user name associated with the effective ID of the process.  If
+@var{string} is not a null pointer, it should be an array that can hold
+at least @code{L_cuserid} characters; the string is returned in this
+array.  Otherwise, a pointer to a string in a static area is returned.
+This string is statically allocated and might be overwritten on
+subsequent calls to this function or to @code{getlogin}.
+@end deftypefun
+
+@comment stdio.h
+@comment POSIX.1
+@deftypevr Macro int L_cuserid
+An integer constant that indicates how long an array you might need to
+store a user name.
+@end deftypevr
+
+These functions let your program identify positively the user who is
+running or the user who logged in this session.  (These can differ when
+setuid programs are involved; @xref{Controlling Access Privileges}.)
+The user cannot do anything to fool these functions.
+
+For most purposes, it is more useful to use the environment variable
+@code{LOGNAME} to find out who the user is.  This is more flexible
+precisely because the user can set @code{LOGNAME} arbitrarily.
+@xref{Environment Variables}.
+
+@node User Database
+@section User Database
+@cindex user database
+@cindex password database
+@pindex /etc/passwd
+
+This section describes all about now to search and scan the database of
+registered users.  The database itself is kept in the file
+@file{/etc/passwd} on most systems, but on some systems a special
+network server gives access to it.
+
+@menu
+* User Data Structure::
+* Lookup User::
+* Scan All Users::
+* Writing a User Entry::
+@end menu
+
+@node User Data Structure
+@subsection The Data Structure that Describes a User
+
+The functions and data structures for accessing the system user database
+are declared in the header file @file{pwd.h}.
+@pindex pwd.h
+
+@comment pwd.h
+@comment POSIX.1
+@deftp {struct Type} passwd
+The @code{passwd} data structure is used to hold information about 
+entries in the system user data base.  It has at least the following members:
+
+@table @code
+@item char *pw_name
+The user's login name.
+
+@item char *pw_passwd.
+The encrypted password string.
+
+@item uid_t pw_uid
+The user ID number.
+
+@item gid_t pw_gid
+The user's default group ID number.
+
+@item char *pw_gecos
+A string typically containing the user's real name, and possibly other
+information such as a phone number.
+
+@item char *pw_dir
+The user's home directory, or initial working directory.  This might be
+a null pointer, in which case the interpretation is system-dependent.
+
+@item char *pw_shell
+The user's default shell, or the initial program run when the user logs in.
+This might be a null pointer, indicating that the system default should
+be used.
+@end table
+@end deftp
+
+@node Lookup User
+@subsection Looking Up One User
+@cindex converting user ID to user name
+@cindex converting user name to user ID
+
+You can search the system user database for information about a
+specific user using @code{getpwuid} or @code{getpwnam}.  These
+functions are declared in @file{pwd.h}.
+
+@comment pwd.h
+@comment POSIX.1
+@deftypefun {struct passwd *} getpwuid (uid_t @var{uid})
+This function returns a pointer to a statically-allocated structure
+containing information about the user whose user ID is @var{uid}.  This
+structure may be overwritten on subsequent calls to @code{getpwuid}.
+
+A null pointer value indicates there is no user in the data base with
+user ID @var{uid}.
+@end deftypefun
+
+@comment pwd.h
+@comment POSIX.1
+@deftypefun {struct passwd *} getpwnam (const char *@var{name})
+This function returns a pointer to a statically-allocated structure
+containing information about the user whose user name is @var{name}.
+This structure may be overwritten on subsequent calls to
+@code{getpwnam}.
+
+A null pointer value indicates there is no user named @var{name}.
+@end deftypefun
+
+@node Scanning All Users
+@subsection Scanning the List of All Users
+@cindex scanning the user list
+
+This section explains how a program can read the list of all users in
+the system, one user at a time.  The functions described here are
+declared in @file{pwd.h}.
+
+The recommended way to scan the users is to open the user file and
+then call @code{fgetgrent} for each successive user:
+
+@comment pwd.h
+@comment SVID
+@deftypefun {struct passwd *} fgetpwent (FILE *@var{stream})
+This function reads the next user entry from @var{stream} and returns a
+pointer to the entry.  The structure is statically allocated and is
+rewritten on subsequent calls to @code{getpwent}.  You must copy the
+contents of the structure if you wish to save the information.
+
+This stream must correspond to a file in the same format as the standard
+password database file.  This function comes from System V.
+@end deftypefun
+
+Another way to scan all the entries in the group database is with
+@code{setpwent}, @code{getpwent}, and @code{endpwent}.  But this method
+is less robust than @code{fgetpwent}, so we provide it only for
+compatibility with SVID.  In particular, these functions are not
+reentrant and are not suitable for use in programs with multiple threads
+of control.  Calling @code{getpwgid} or @code{getpwnam} can also confuse
+the internal state of these functions.
+
+@comment pwd.h
+@comment SVID, GNU
+@deftypefun void setpwent ()
+This function initializes a stream which @code{getpwent} uses to read
+the user database.
+@end deftypefun
+
+@comment pwd.h
+@comment SVID, GNU
+@deftypefun {struct passwd *} getpwent ()
+The @code{getpwent} function reads the next entry from the stream
+initialized by @code{setpwent}.  It returns a pointer to the entry.  The
+structure is statically allocated and is rewritten on subsequent calls
+to @code{getpwent}.  You must copy the contents of the structure if you
+wish to save the information.
+@end deftypefun
+
+@comment pwd.h
+@comment SVID, GNU
+@deftypefun void endpwent ()
+This function closes the internal stream used by @code{getpwent}.
+@end deftypefun
+
+@node Writing a User Entry
+@subsection Writing a User Entry
+
+@comment pwd.h
+@comment SVID
+@deftypefun int putpwent (const struct passwd *@var{p}, FILE *@var{stream})
+This function writes the user entry @code{*@var{p}} to the stream
+@var{stream}, in the format used for the standard user database
+file.  The return value is zero on success and non-zero on failure.
+
+This function exists for compatibility with SVID.  We recommend that you
+avoid using it, because it makes sense only on the assumption that the
+@code{struct passwd} structure has no members except the standard ones;
+on a system which merges the traditional Unix data base with other
+extended information about users, adding an entry using this function
+would inevitably leave out much of the important information.
+
+The function @code{putpwent} is declared in @file{pwd.h}.
+@end deftypefun
+
+@node Group Database
+@section Group Database
+@cindex group database
+@pindex /etc/group
+
+This section describes all about now to search and scan the database of
+registered groups.  The database itself is kept in the file
+@file{/etc/group} on most systems, but on some systems a special network
+service provides access to it.
+
+@menu
+* Group Data Structure::
+* Lookup Group::
+* Scan All Groups::
+@end menu
+
+@node Group Data Structure
+@subsection The Data Structure for a Group
+
+The functions and data structures for accessing the system group
+database are declared in the header file @file{grp.h}.
+@pindex grp.h
+
+@comment grp.h
+@comment POSIX.1
+@deftp {Data Type} {struct group} 
+The @code{group} structure is used to hold information about an entry in
+the system group database.  It has at least the following members:
+
+@table @code
+@item char *gr_name
+The name of the group.
+
+@item gid_t gr_gid
+The group ID of the group.
+
+@item char **gr_mem
+A vector of pointers to the names of users in the group.  Each user name
+is a null-terminated string, and the vector itself is terminated by a
+null pointer.
+@end table
+@end deftp
+
+@node Lookup Group
+@subsection Looking Up One Group
+@cindex converting group name to group ID
+@cindex converting group ID to group name
+
+You can search the group database for information about a specific
+group using @code{getgrgid} or @code{getgrnam}.  These functions are
+declared in @file{grp.h}.
+
+@comment grp.h
+@comment POSIX.1
+@deftypefun {struct group *} getgrgid (gid_t @var{gid})
+This function returns a pointer to a statically-allocated structure
+containing information about the group whose group ID is @var{gid}.
+This structure may be overwritten by subsequent calls to
+@code{getgrgid}.
+
+A null pointer indicates there is no group with ID @var{gid}.
+@end deftypefun
+
+@comment grp.h
+@comment POSIX.1
+@deftypefun {struct group *} getgrnam (const char *@var{name})
+This function returns a pointer to a statically-allocated structure
+containing information about the group whose group name is @var{name}.
+This structure may be overwritten by subsequent calls to
+@code{getgrnam}.
+
+A null pointer indicates there is no group named @var{name}.
+@end deftypefun
+
+@node Scanning All Groups
+@subsection Scanning the List of All Groups
+@cindex scanning the group list
+
+This section explains how a program can read the list of all groups in
+the system, one group at a time.  The functions described here are
+declared in @file{grp.h}.
+
+The recommended way to scan the groups is to open the group file and
+then call @code{fgetgrent} for each successive group:
+
+@comment grp.h
+@comment SVID
+@deftypefun {struct group *} fgetgrent (FILE *@var{stream})
+The @code{fgetgrent} function reads the next entry from @var{stream}.
+It returns a pointer to the entry.  The structure is statically
+allocated and is rewritten on subsequent calls to @code{getgrent}.  You
+must copy the contents of the structure if you wish to save the
+information.
+
+The stream must correspond to a file in the same format as the standard
+group database file.
+@end deftypefun
+
+Another way to scan all the entries in the group database is with
+@code{setgrent}, @code{getgrent}, and @code{endgrent}.  But this method
+is less robust than @code{fgetgrent}, so we provide it only for
+compatibility with SVID.  In particular, these functions are not
+reentrant and are not suitable for use in programs with multiple threads
+of control.  Calling @code{getgrgid} or @code{getgrnam} can also confuse
+the internal state of these functions.
+
+@comment grp.h
+@comment SVID, GNU
+@deftypefun void setgrent ()
+This function initializes a stream for reading from the group data base.
+You use this stream by calling @code{getgrent}.
+@end deftypefun
+
+@comment grp.h
+@comment SVID, GNU
+@deftypefun {struct group *} getgrent ()
+The @code{getgrent} function reads the next entry from the stream
+initialized by @code{setgrent}.  It returns a pointer to the entry.  The
+structure is statically allocated and is rewritten on subsequent calls
+to @code{getgrent}.  You must copy the contents of the structure if you
+wish to save the information.
+@end deftypefun
+
+@comment grp.h
+@comment SVID, GNU
+@deftypefun void endgrent ()
+This function closes the internal stream used by @code{getgrent}.
+@end deftypefun
+
+@node Database Example
+@section User and Group Database Example
+
+Here is an example program showing the use of the system database inquiry
+functions.  The program prints some information about the user running
+the program.
+
+@example
+#include <grp.h>
+#include <pwd.h>
+#include <sys/types.h>
+#include <unistd.h>
+#include <stdlib.h>
+
+void
+main ()
+@{
+  uid_t me;
+  struct passwd *my_passwd;
+  struct group *my_group;
+  char **members;
+
+  /* @r{Get information about the user ID.} */
+  me = getuid ();
+  my_passwd = getpwuid (me);
+  if (!my_passwd) @{
+    printf ("Couldn't find out about user %d.\n", me);
+    exit (EXIT_FAILURE);
+    @}
+
+  /* @r{Print the information.} */
+  printf ("My login name is %s.\n", my_passwd->pw_name);
+  printf ("My uid is %d.\n", my_passwd->pw_uid);
+  printf ("My home directory is %s.\n", my_passwd->pw_dir);
+  printf ("My default shell is %s.\n", my_passwd->pw_shell);
+
+  /* @r{Get information about the default group ID.} */
+  my_group = getgrgid (my_passwd->pw_gid);
+  if (!my_group) @{
+    printf ("Couldn't find out about group %d.\n",
+            my_passwd->pw_gid);
+    exit (EXIT_FAILURE);
+    @}
+
+  /* @r{Print the information.} */
+  printf ("My default group is %s (%d).\n",
+	  my_group->gr_name, my_passwd->pw_gid);
+  printf ("The members of this group are:\n");
+  members = my_group->gr_mem;
+  while (*members)
+    printf ("  %s\n", *members++);
+
+  exit (EXIT_SUCCESS);
+@}
+@end example
+
+Here is some output from this program:
+
+@example
+My login name is snurd.
+My uid is 31093.
+My home directory is /home/fsg/snurd.
+My default shell is /bin/sh.
+My default group is guest (12).
+The members of this group are:
+  friedman
+  @dots{}
+@end example