On Wed, May 15, 2013 at 06:16:55AM -0600, Todd C. Miller wrote:
> I've taken your diff and merged some useful bits from FreeBSD.
> Specifically, the use of getline() and multibyte support for the
> -d option.

I asked stsp@ about the multibyte support yesterday and it was his
opinion that it's not currently needed.

Some comments inline based on what I've developed further.

> I also made the functions non-static (though I don't think that is
> such a big deal) and tweaked the manual to treat '-' like cat(1)
> as per j...@wxcvbn.org's comments.
> 
>  - todd
> 
> Index: Makefile
> ===================================================================
> RCS file: /home/cvs/openbsd/src/usr.bin/Makefile,v
> retrieving revision 1.129
> diff -u -r1.129 Makefile
> --- Makefile  15 Mar 2013 06:01:41 -0000      1.129
> +++ Makefile  15 May 2013 11:43:52 -0000
> @@ -16,7 +16,7 @@
>       m4 mail make man mandoc mesg mg \
>       midiplay mixerctl mkdep mklocale mkstr mktemp modstat nc netstat \
>       newsyslog \
> -     nfsstat nice nm nohup oldrdist pagesize passwd paste patch pctr \
> +     nfsstat nice nm nl nohup oldrdist pagesize passwd paste patch pctr \
>       pkg-config pkill \
>       pr printenv printf quota radioctl ranlib rcs rdist rdistd \
>       readlink renice rev rpcgen rpcinfo rs rsh rup ruptime rusers rwall \
> Index: nl/Makefile
> ===================================================================
> RCS file: nl/Makefile
> diff -N nl/Makefile
> --- /dev/null 1 Jan 1970 00:00:00 -0000
> +++ nl/Makefile       15 May 2013 11:43:52 -0000
> @@ -0,0 +1,6 @@
> +#    $OpenBSD$
> +#    $NetBSD: Makefile,v 1.4 2011/08/16 12:00:46 christos Exp $
> +
> +PROG=        nl
> +
> +.include <bsd.prog.mk>
> Index: nl/nl.1
> ===================================================================
> RCS file: nl/nl.1
> diff -N nl/nl.1
> --- /dev/null 1 Jan 1970 00:00:00 -0000
> +++ nl/nl.1   15 May 2013 12:11:12 -0000
> @@ -0,0 +1,216 @@
> +.\"  $OpenBSD$
> +.\"  $NetBSD: nl.1,v 1.12 2012/04/08 22:00:39 wiz Exp $
> +.\"
> +.\" Copyright (c) 1999 The NetBSD Foundation, Inc.
> +.\" All rights reserved.
> +.\"
> +.\" This code is derived from software contributed to The NetBSD Foundation
> +.\" by Klaus Klein.
> +.\"
> +.\" Redistribution and use in source and binary forms, with or without
> +.\" modification, are permitted provided that the following conditions
> +.\" are met:
> +.\" 1. Redistributions of source code must retain the above copyright
> +.\"    notice, this list of conditions and the following disclaimer.
> +.\" 2. Redistributions in binary form must reproduce the above copyright
> +.\"    notice, this list of conditions and the following disclaimer in the
> +.\"    documentation and/or other materials provided with the distribution.
> +.\"
> +.\" THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
> +.\" ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
> LIMITED
> +.\" TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A 
> PARTICULAR
> +.\" PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
> +.\" BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
> +.\" CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
> +.\" SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
> +.\" INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
> +.\" CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
> +.\" ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF 
> THE
> +.\" POSSIBILITY OF SUCH DAMAGE.
> +.\"
> +.Dd $Mdocdate$
> +.Dt NL 1
> +.Os
> +.Sh NAME
> +.Nm nl
> +.Nd line numbering filter
> +.Sh SYNOPSIS
> +.Nm
> +.Op Fl p
> +.Op Fl b Ar type
> +.Op Fl d Ar delim
> +.Op Fl f Ar type
> +.Op Fl h Ar type
> +.Op Fl i Ar incr
> +.Op Fl l Ar num
> +.Op Fl n Ar format
> +.Op Fl s Ar sep
> +.Op Fl v Ar startnum
> +.Op Fl w Ar width
> +.Op Ar file
> +.Sh DESCRIPTION
> +The
> +.Nm
> +utility reads lines from the named
> +.Ar file ,
> +applies a configurable line numbering filter operation and writes the result
> +to the standard output.
> +If
> +.Ar file
> +is a single dash
> +.Pq Sq \&-
> +or absent,
> +.Nm
> +reads from the standard input.
> +.Pp
> +The
> +.Nm
> +utility treats the text it reads in terms of logical pages.
> +Unless specified otherwise, line numbering is reset at the start of each
> +logical page.
> +A logical page consists of a header, a body and a footer section; empty
> +sections are valid.
> +Different line numbering options are independently available for header,
> +body and footer sections.
> +.Pp
> +The starts of logical page sections are signaled by input lines containing
> +nothing but one of the following sequences of delimiter characters:
> +.Bd -unfilled -offset indent
> +.Bl -column "\e:\e:\e: " "header "
> +.It Em "Line"        "Start of"
> +.It \e:\e:\e:        header
> +.It \e:\e:   body
> +.It \e:      footer
> +.El
> +.Ed
> +.Pp
> +If the input does not contain any logical page section signaling directives,
> +the text being read is assumed to consist of a single logical page body.
> +.Pp
> +The following options are available:
> +.Bl -tag -width indent
> +.It Fl b Ar type
> +Specify the logical page body lines to be numbered.
> +Recognized
> +.Ar type
> +arguments are:
> +.Bl -tag -width pstringXX
> +.It a
> +Number all lines.
> +.It t
> +Number only non-empty lines.
> +.It n
> +No line numbering.
> +.It p Ns Ar expr
> +Number only those lines that contain the basic regular expression specified
> +by
> +.Ar expr .
> +.El
> +.Pp
> +The default
> +.Ar type
> +for logical page body lines is t.
> +.It Fl d Ar delim
> +Specify the delimiter characters used to indicate the start of a logical
> +page section in the input file.
> +At most two characters may be specified; if only one character is specified,
> +the first character is replaced and the second character remains unchanged.
> +The default
> +.Ar delim
> +characters are ``\e:''.
> +.It Fl f Ar type
> +Specify the same as
> +.Fl b Ar type
> +except for logical page footer lines.
> +The default
> +.Ar type
> +for logical page footer lines is n.
> +.It Fl h Ar type
> +Specify the same as
> +.Fl b Ar type
> +except for logical page header lines.
> +The default
> +.Ar type
> +for logical page header lines is n.
> +.It Fl i Ar incr
> +Specify the increment value used to number logical page lines.
> +The default
> +.Ar incr
> +value is 1.
> +.It Fl l Ar num
> +If numbering of all lines is specified for the current logical section
> +using the corresponding
> +.Fl b
> +a,
> +.Fl f
> +a
> +or
> +.Fl h
> +a
> +option,
> +specify the number of adjacent blank lines to be considered as one.
> +For example,
> +.Fl l
> +2 results in only the second adjacent blank line being numbered.
> +The default
> +.Ar num
> +value is 1.
> +.It Fl n Ar format
> +Specify the line numbering output format.
> +Recognized
> +.Ar format
> +arguments are:
> +.Bl -tag -width lnXX -compact
> +.It ln
> +Left justified.
> +.It rn
> +Right justified, leading zeros suppressed.
> +.It rz
> +Right justified, leading zeros kept.
> +.El
> +.Pp
> +The default
> +.Ar format
> +is rn.
> +.It Fl p
> +Specify that line numbering should not be restarted at logical page 
> delimiters.
> +.It Fl s Ar sep
> +Specify the characters used in separating the line number and the 
> corresponding
> +text line.
> +The default
> +.Ar sep
> +setting is a single tab character.
> +.It Fl v Ar startnum
> +Specify the initial value used to number logical page lines; see also the
> +description of the
> +.Fl p
> +option.
> +The default
> +.Ar startnum
> +value is 1.
> +.It Fl w Ar width
> +Specify the number of characters to be occupied by the line number;
> +in case the
> +.Ar width
> +is insufficient to hold the line number, it will be truncated to its
> +.Ar width
> +least significant digits.
> +The default
> +.Ar width
> +is 6.
> +.El
> +.Sh EXIT STATUS
> +.Ex -std
> +.Sh SEE ALSO
> +.Xr pr 1
> +.Sh STANDARDS
> +The
> +.Nm
> +utility is compliant with the
> +.St -p1003.1-2008
> +specification.
> +.Sh HISTORY
> +The
> +.Nm
> +utility first appeared in
> +.At V.2 .
> Index: nl/nl.c
> ===================================================================
> RCS file: nl/nl.c
> diff -N nl/nl.c
> --- /dev/null 1 Jan 1970 00:00:00 -0000
> +++ nl/nl.c   15 May 2013 12:07:35 -0000
> @@ -0,0 +1,394 @@
> +/*   $OpenBSD$ */
> +/*   $NetBSD: nl.c,v 1.11 2011/08/16 12:00:46 christos Exp $ */
> +
> +/*-
> + * Copyright (c) 1999 The NetBSD Foundation, Inc.
> + * All rights reserved.
> + *
> + * This code is derived from software contributed to The NetBSD Foundation
> + * by Klaus Klein.
> + *
> + * Redistribution and use in source and binary forms, with or without
> + * modification, are permitted provided that the following conditions
> + * are met:
> + * 1. Redistributions of source code must retain the above copyright
> + *    notice, this list of conditions and the following disclaimer.
> + * 2. Redistributions in binary form must reproduce the above copyright
> + *    notice, this list of conditions and the following disclaimer in the
> + *    documentation and/or other materials provided with the distribution.
> + *
> + * THIS SOFTWARE IS PROVIDED BY THE NETBSD FOUNDATION, INC. AND CONTRIBUTORS
> + * ``AS IS'' AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT 
> LIMITED
> + * TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
> + * PURPOSE ARE DISCLAIMED.  IN NO EVENT SHALL THE FOUNDATION OR CONTRIBUTORS
> + * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
> + * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
> + * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
> + * INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
> + * CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
> + * ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
> + * POSSIBILITY OF SUCH DAMAGE.
> + */
> +
> +#include <sys/cdefs.h>
> +
> +#include <err.h>
> +#include <errno.h>
> +#include <limits.h>
> +#include <locale.h>
> +#include <regex.h>
> +#include <stdio.h>
> +#include <stdlib.h>
> +#include <string.h>
> +#include <unistd.h>
> +#include <wchar.h>
> +
> +typedef enum {
> +     number_all,             /* number all lines */
> +     number_nonempty,        /* number non-empty lines */
> +     number_none,            /* no line numbering */
> +     number_regex            /* number lines matching regular expression */
> +} numbering_type;
> +
> +struct numbering_property {
> +     const char * const      name;           /* for diagnostics */
> +     numbering_type          type;           /* numbering type */
> +     regex_t                 expr;           /* for type == number_regex */
> +};
> +
> +/* line numbering formats */
> +#define FORMAT_LN    "%-*d"  /* left justified, leading zeros suppressed */
> +#define FORMAT_RN    "%*d"   /* right justified, leading zeros suppressed */
> +#define FORMAT_RZ    "%0*d"  /* right justified, leading zeros kept */
> +
> +#define FOOTER               0
> +#define BODY         1
> +#define HEADER               2
> +#define NP_LAST              HEADER
> +
> +static struct numbering_property numbering_properties[NP_LAST + 1] = {
> +     { "footer",     number_none,    { 0, 0, 0, 0 } },
> +     { "body",       number_nonempty, { 0, 0, 0, 0 } },
> +     { "header",     number_none,    { 0, 0, 0, 0 } },
> +};
> +
> +#define max(a, b)    ((a) > (b) ? (a) : (b))
> +
> +/*
> + * Maximum number of characters required for a decimal representation of a
> + * (signed) int; courtesy of tzcode.
> + */
> +#define INT_STRLEN_MAXIMUM \
> +     ((sizeof (int) * CHAR_BIT - 1) * 302 / 1000 + 2)

This int dance can be removed and it gets rid of a lot of unndeeded
complexity in this program. The standard doesn't specify what should
happen when the number of characters in a number exceeds 'width'. It's
OK to write numbers such as "123" even if 'width' == 2.

> +
> +void         filter(void);
> +void         parse_numbering(const char *, int);
> +__dead void  usage(void);
> +
> +/*
> + * Dynamically allocated buffer suitable for string representation of ints.
> + */
> +static char *intbuffer;
> +static size_t intbuffersize;

These can be removed because int dance is not needed.

> +
> +/*
> + * Delimiter characters that indicate the start of a logical page section.
> + */
> +static char delim[2 * MB_LEN_MAX];
> +static int delimlen;
> +
> +/*
> + * Configurable parameters.
> + */
> +
> +/* line numbering format */
> +static const char *format = FORMAT_RN;
> +
> +/* increment value used to number logical page lines */
> +static int incr = 1;
> +
> +/* number of adjacent blank lines to be considered (and numbered) as one */
> +static unsigned int nblank = 1;
> +
> +/* whether to restart numbering at logical page delimiters */
> +static int restart = 1;
> +
> +/* characters used in separating the line number and the corrsp. text line */
> +static const char *sep = "\t";
> +
> +/* initial value used to number logical page lines */
> +static int startnum = 1;
> +
> +/* number of characters to be used for the line number */
> +/* should be unsigned but required signed by `*' precision conversion */
> +static int width = 6;
> +
> +
> +int
> +main(int argc, char *argv[])
> +{
> +     int c;
> +     size_t clen;
> +     char delim1[MB_LEN_MAX] = { '\\' }, delim2[MB_LEN_MAX] = { ':' };
> +     size_t delim1len = 1, delim2len = 1;
> +     const char *errstr;
> +
> +     (void)setlocale(LC_ALL, "");
> +
> +     while ((c = getopt(argc, argv, "pb:d:f:h:i:l:n:s:v:w:")) != -1) {
> +             switch (c) {
> +             case 'p':
> +                     restart = 0;
> +                     break;
> +             case 'b':
> +                     parse_numbering(optarg, BODY);
> +                     break;
> +             case 'd':
> +                     clen = mbrlen(optarg, MB_CUR_MAX, NULL);
> +                     if (clen == (size_t)-1 || clen == (size_t)-2) {
> +                             errno = EILSEQ;
> +                             err(EXIT_FAILURE, NULL);
> +                     }
> +                     if (clen != 0) {
> +                             memcpy(delim1, optarg, delim1len = clen);
> +                             clen = mbrlen(optarg + delim1len,
> +                                 MB_CUR_MAX, NULL);
> +                             if (clen == (size_t)-1 || clen == (size_t)-2) {
> +                                     errno = EILSEQ;
> +                                     err(EXIT_FAILURE, NULL);
> +                             }
> +                             if (clen != 0) {
> +                                     memcpy(delim2, optarg + delim1len,
> +                                         delim2len = clen);
> +                                     if (optarg[delim1len + clen] != '\0') {
> +                                             errx(EXIT_FAILURE,
> +                                                 "invalid delimiter: %s",
> +                                                 optarg);
> +                                     }
> +                             }
> +                     }
> +                     break;
> +             case 'f':
> +                     parse_numbering(optarg, FOOTER);
> +                     break;
> +             case 'h':
> +                     parse_numbering(optarg, HEADER);
> +                     break;
> +             case 'i':
> +                     incr = strtonum(optarg, INT_MIN, INT_MAX, &errstr);
> +                     if (errstr)
> +                             errx(EXIT_FAILURE, "increment value is %s: %s",
> +                                 errstr, optarg);
> +                     break;
> +             case 'l':
> +                     nblank = strtonum(optarg, 0, UINT_MAX, &errstr);
> +                     if (errstr)
> +                             errx(EXIT_FAILURE,
> +                                 "blank line value is %s: %s",
> +                                 errstr, optarg);
> +                     break;
> +             case 'n':
> +                     if (strcmp(optarg, "ln") == 0) {
> +                             format = FORMAT_LN;
> +                     } else if (strcmp(optarg, "rn") == 0) {
> +                             format = FORMAT_RN;
> +                     } else if (strcmp(optarg, "rz") == 0) {
> +                             format = FORMAT_RZ;
> +                     } else
> +                             errx(EXIT_FAILURE,
> +                                 "illegal format -- %s", optarg);
> +                     break;
> +             case 's':
> +                     sep = optarg;
> +                     break;
> +             case 'v':
> +                     startnum = strtonum(optarg, INT_MIN, INT_MAX, &errstr);
> +                     if (errstr)
> +                             errx(EXIT_FAILURE,
> +                                 "initial logical page value is %s: %s",
> +                                 errstr, optarg);
> +                     break;
> +             case 'w':
> +                     width = strtonum(optarg, 1, INT_MAX, &errstr);
> +                     if (errstr)
> +                             errx(EXIT_FAILURE, "width is %s: %s", errstr,
> +                                 optarg);
> +                     break;
> +             case '?':
> +             default:
> +                     usage();
> +                     /* NOTREACHED */
> +             }
> +     }
> +     argc -= optind;
> +     argv += optind;
> +
> +     switch (argc) {
> +     case 0:
> +             break;
> +     case 1:
> +             if (strcmp(argv[0], "-") != 0 &&
> +                 freopen(argv[0], "r", stdin) == NULL)
> +                     err(EXIT_FAILURE, "%s", argv[0]);
> +             break;
> +     default:
> +             usage();
> +             /* NOTREACHED */
> +     }
> +
> +     /* Generate the delimiter sequence */
> +     memcpy(delim, delim1, delim1len);
> +     memcpy(delim + delim1len, delim2, delim2len);
> +     delimlen = delim1len + delim2len;
> +
> +     /* Allocate a buffer suitable for preformatting line number. */
> +     intbuffersize = max((int)INT_STRLEN_MAXIMUM, width) + 1; /* NUL */
> +     if ((intbuffer = malloc(intbuffersize)) == NULL)
> +             err(EXIT_FAILURE, "cannot allocate preformatting buffer");

Int dance requires these mallocs and they can be removed.

> +
> +     /* Do the work. */
> +     filter();
> +
> +     exit(EXIT_SUCCESS);
> +}
> +
> +void
> +filter(void)
> +{
> +     char *buffer;
> +     size_t buffersize;
> +     ssize_t linelen;
> +     int line;               /* logical line number */
> +     int section;            /* logical page section */
> +     unsigned int adjblank;  /* adjacent blank lines */
> +     int consumed;           /* intbuffer measurement */
> +     int donumber = 0, idx;
> +
> +     adjblank = 0;
> +     line = startnum;
> +     section = BODY;
> +
> +     buffer = NULL;
> +     buffersize = 0;
> +     while ((linelen = getline(&buffer, &buffersize, stdin)) > 0) {
> +             for (idx = FOOTER; idx <= NP_LAST; idx++) {
> +                     /* Does it look like a delimiter? */
> +                     if (delimlen * (idx + 1) > linelen)
> +                             break;
> +                     if (memcmp(buffer + delimlen * idx, delim,
> +                         delimlen) != 0)
> +                             break;
> +                     /* Was this the whole line? */
> +                     if (buffer[delimlen * (idx + 1)] == '\n') {
> +                             section = idx;
> +                             adjblank = 0;
> +                             if (restart)
> +                                     line = startnum;
> +                             goto nextline;
> +                     }
> +             }
> +
> +             switch (numbering_properties[section].type) {
> +             case number_all:
> +                     /*
> +                      * Doing this for number_all only is disputable, but
> +                      * the standard expresses an explicit dependency on
> +                      * `-b a' etc.
> +                      */
> +                     if (buffer[0] == '\n' && ++adjblank < nblank)
> +                             donumber = 0;
> +                     else
> +                             donumber = 1, adjblank = 0;
> +                     break;
> +             case number_nonempty:
> +                     donumber = (buffer[0] != '\n');
> +                     break;
> +             case number_none:
> +                     donumber = 0;
> +                     break;
> +             case number_regex:
> +                     donumber =
> +                         (regexec(&numbering_properties[section].expr,
> +                         buffer, 0, NULL, 0) == 0);
> +                     break;
> +             }
> +
> +             if (donumber) {
> +                     consumed = snprintf(intbuffer, intbuffersize, format,
> +                         width, line);
> +                     (void)printf("%s",
> +                         intbuffer + max(0, consumed - width));
> +                     line += incr;
> +             } else {
> +                     (void)printf("%*s", width, "");
> +             }

snprintf is needed for the int dance and can be removed / simplified to:

    if (donumber) {
        (void)printf(format, width, line);
        line += incr;
    } else
        (void)printf("%*s", width, "");

> +             (void)printf("%s%s", sep, buffer);

This is incorrect. It's a bug in Free/NetBSD implementation. The
standard says that:

     When line numbers are suppressed for a portion of the page; the
     <separator> is also suppressed.

I've been thinking how this should be correctly implemented but I
haven't figured it out yet.

> +
> +             if (ferror(stdout))
> +                     err(EXIT_FAILURE, "output error");
> +nextline:
> +             ;
> +     }
> +
> +     if (ferror(stdin))
> +             err(EXIT_FAILURE, "input error");
> +
> +     free(buffer);
> +}
> +
> +/*
> + * Various support functions.
> + */
> +
> +void
> +parse_numbering(const char *argstr, int section)
> +{
> +     int error;
> +     char errorbuf[NL_TEXTMAX];
> +
> +     switch (argstr[0]) {
> +     case 'a':
> +             numbering_properties[section].type = number_all;
> +             break;
> +     case 'n':
> +             numbering_properties[section].type = number_none;
> +             break;
> +     case 't':
> +             numbering_properties[section].type = number_nonempty;
> +             break;
> +     case 'p':
> +             /* If there was a previous expression, throw it away. */
> +             if (numbering_properties[section].type == number_regex)
> +                     regfree(&numbering_properties[section].expr);
> +             else
> +                     numbering_properties[section].type = number_regex;
> +
> +             /* Compile/validate the supplied regular expression. */
> +             if ((error = regcomp(&numbering_properties[section].expr,
> +                 &argstr[1], REG_NEWLINE|REG_NOSUB)) != 0) {
> +                     (void)regerror(error,
> +                         &numbering_properties[section].expr,
> +                         errorbuf, sizeof (errorbuf));
> +                     errx(EXIT_FAILURE,
> +                         "%s expr: %s -- %s",
> +                         numbering_properties[section].name, errorbuf,
> +                         &argstr[1]);
> +             }
> +             break;
> +     default:
> +             errx(EXIT_FAILURE,
> +                 "illegal %s line numbering type -- %s",
> +                 numbering_properties[section].name, argstr);
> +     }
> +}
> +
> +__dead void
> +usage(void)
> +{
> +     extern char *__progname;
> +
> +     (void)fprintf(stderr, "usage: %s [-p] [-b type] [-d delim] [-f type] "
> +         "[-h type] [-i incr] [-l num]\n\t[-n format] [-s sep] "
> +         "[-v startnum] [-w width] [file]\n", __progname);
> +     exit(EXIT_FAILURE);
> +}
> 

Reply via email to