sounds reasonable.

On Mon, Mar 12, 2012 at 02:18:42PM +0100, Mark Kettenis wrote:
> > Date: Mon, 12 Mar 2012 13:45:58 +0100
> > From: Jan Klemkow <[email protected]>
> > 
> > The return of EINVAL on fp NULL comes from me.  I think its even an
> > invalid argument like the other pointers.
> 
> We tend to take the position that it is better to crash hard that
> return an error if an invalid argument gets passed to a library
> function.  Returning an error just hides bugs because people forget to
> check return values.  POSIX demands that we return EINVAL if lineptr
> or n is NULL, so we have to do that even though we consider it to be
> unhelpful.  But since it doesn't say anything about fp I think you
> should drop the fp == NULL check.

Index: stdio/Makefile.inc
===================================================================
RCS file: /cvs/src/lib/libc/stdio/Makefile.inc,v
retrieving revision 1.21
diff -u -p -r1.21 Makefile.inc
--- stdio/Makefile.inc  18 Jan 2012 14:01:38 -0000      1.21
+++ stdio/Makefile.inc  12 Mar 2012 13:39:18 -0000
@@ -18,12 +18,14 @@ SRCS+=      asprintf.c clrerr.c fclose.c fdop
        fgetwc.c fgetws.c fputwc.c fputws.c fwide.c getwc.c getwchar.c \
        putwc.c putwchar.c ungetwc.c \
        fwprintf.c swprintf.c vfwprintf.c vswprintf.c vwprintf.c wprintf.c \
-       fwscanf.c swscanf.c vfwscanf.c vswscanf.c vwscanf.c wscanf.c
+       fwscanf.c swscanf.c vfwscanf.c vswscanf.c vwscanf.c wscanf.c \
+       getdelim.c
 
 MAN+=  fclose.3 ferror.3 fflush.3 fgetln.3 fgets.3 fopen.3 fputs.3 \
        fread.3 fseek.3 funopen.3 getc.3 mktemp.3 perror.3 printf.3 putc.3 \
        remove.3 scanf.3 setbuf.3 stdio.3 tmpnam.3 ungetc.3 \
-       fgetws.3 fputws.3 fwide.3 getwc.3 putwc.3 ungetwc.3 wprintf.3 wscanf.3
+       fgetws.3 fputws.3 fwide.3 getwc.3 putwc.3 ungetwc.3 wprintf.3 wscanf.3 \
+       getdelim.3
 
 MLINKS+=ferror.3 clearerr.3 ferror.3 feof.3 ferror.3 fileno.3
 MLINKS+=fflush.3 fpurge.3
Index: stdio/getdelim.3
===================================================================
RCS file: stdio/getdelim.3
diff -N stdio/getdelim.3
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ stdio/getdelim.3    12 Mar 2012 13:39:18 -0000
@@ -0,0 +1,156 @@
+.\"     $OpenBSD$
+.\"     $NetBSD: getdelim.3,v 1.9 2011/04/20 23:37:51 enami Exp $
+.\"
+.\" Copyright (c) 2009 The NetBSD Foundation, Inc.
+.\" All rights reserved.
+.\"
+.\" This code is derived from software contributed to The NetBSD Foundation
+.\" by Roy Marples.
+.\"
+.\" 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 GETDELIM 3
+.Os
+.Sh NAME
+.Nm getdelim ,
+.Nm getline
+.Nd read a delimited record from a stream
+.Sh LIBRARY
+.Lb libc
+.Sh SYNOPSIS
+.In stdio.h
+.Ft ssize_t
+.Fn getdelim "char ** restrict lineptr" "size_t * restrict n" "int delimiter" 
"FILE * restrict stream"
+.Ft ssize_t
+.Fn getline "char ** restrict lineptr" "size_t * restrict n" "FILE * restrict 
stream"
+.Sh DESCRIPTION
+The
+.Fn getdelim
+function reads from the
+.Fa stream
+until it encounters a character matching
+.Fa delimiter ,
+storing the input in
+.Fa *lineptr .
+The buffer is
+.Dv NUL Ns No -terminated
+and includes the delimiter.
+The
+.Fa delimiter
+character must be representable as an unsigned char.
+.Pp
+If
+.Fa *n
+is non-zero, then
+.Fa *lineptr
+must be pre-allocated to at least
+.Fa *n
+bytes.
+The buffer should be allocated dynamically;
+it must be possible to
+.Xr free 3
+.Fa *lineptr .
+.Fn getdelim
+ensures that
+.Fa *lineptr
+is large enough to hold the input, updating
+.Fa *n
+to reflect the new size.
+.Pp
+The
+.Fn getline
+function is equivalent to
+.Fn getdelim
+with
+.Fa delimiter
+set to the newline character.
+.Sh RETURN VALUES
+The
+.Fn getdelim
+and
+.Fn getline
+functions return the number of characters read, including the delimiter.
+If no characters were read and the stream is at end-of-file, the functions
+return \-1.
+If an error occurs, the functions return \-1 and the global variable
+.Va errno
+is set to indicate the error.
+.Pp
+The functions do not distinguish between end-of-file and error,
+and callers must use
+.Xr feof 3
+and
+.Xr ferror 3
+to determine which occurred.
+.Sh EXAMPLES
+The following code fragment reads lines from a file and writes them to
+standard output.
+.Bd -literal -offset indent
+char *line = NULL;
+size_t linesize = 0;
+ssize_t linelen;
+
+while ((linelen = getline(\*[Am]line, \*[Am]linesize, fp)) != -1)
+       fwrite(line, linelen, 1, stdout);
+
+if (ferror(fp))
+       perror("getline");
+.Ed
+.Sh ERRORS
+.Bl -tag -width [EOVERFLOW]
+.It Bq Er EINVAL
+.Fa lineptr
+or
+.Fa n
+is a
+.Dv NULL
+pointer.
+.It Bq Er EOVERFLOW
+More than SSIZE_MAX characters were read without encountering the delimiter.
+.El
+.Pp
+The
+.Fn getdelim
+and
+.Fn getline
+functions may also fail and set
+.Va errno
+for any of the errors specified in the routines
+.Xr fflush 3 ,
+.Xr malloc 3 ,
+.Xr read 2 ,
+.Xr stat 2 ,
+or
+.Xr realloc 3 .
+.Sh SEE ALSO
+.Xr ferror 3 ,
+.Xr fgets 3 ,
+.Xr fgetln 3 ,
+.Xr fopen 3
+.Sh STANDARDS
+The
+.Fn getdelim
+and
+.Fn getline
+functions conform to
+.St -p1003.1-2008 .
Index: stdio/getdelim.c
===================================================================
RCS file: stdio/getdelim.c
diff -N stdio/getdelim.c
--- /dev/null   1 Jan 1970 00:00:00 -0000
+++ stdio/getdelim.c    12 Mar 2012 13:39:18 -0000
@@ -0,0 +1,107 @@
+/* $OpenBSD$ */
+
+/*
+ * Copyright (c) 2012 Jan Klemkow <[email protected]>
+ *
+ * Permission to use, copy, modify, and distribute this software for any
+ * purpose with or without fee is hereby granted, provided that the above
+ * copyright notice and this permission notice appear in all copies.
+ *
+ * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
+ * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
+ * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
+ * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
+ * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
+ * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
+ * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
+ */
+
+#include <errno.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <limits.h>
+#include "local.h"
+
+ssize_t
+getdelim(char** lineptr, size_t* n, int delim, FILE* fp)
+{
+       unsigned char* c;
+       char* newp;
+       size_t len = 0;
+       size_t offset = 0;
+
+       if (lineptr == NULL || n == NULL) {
+               errno = EINVAL;
+               return -1;
+       }
+
+       _SET_ORIENTATION(fp, -1);
+       FLOCKFILE(fp);
+       do {
+               /* if file buffer is empty, refill it */
+               if (fp->_r <= 0 && __srefill(fp) == EOF && __sferror(fp)) {
+                       fp->_flags |= __SERR;
+                       return -1;
+               }
+
+               /* search for delimiter in fp buffer */
+               if ((c = memchr((void*)fp->_p, delim, (size_t)fp->_r)) == NULL){
+                       /* delimiter not found */
+                       len = (size_t) fp->_r;
+               } else {
+                       /* found delimiter */
+                       len = (size_t) (c - (unsigned char*) fp->_p);
+                       len++;
+               }
+
+               /* checking for enought space in buffer lineptr */
+               if (*n < offset + len + 1) {
+                       if (offset + len > SSIZE_MAX) {
+                               FUNLOCKFILE(fp);
+                               errno = EOVERFLOW;
+                               return -1;
+                       }
+
+                       /* duplicate the buffersize of line */
+                       *n <<= 1;
+
+                       /*
+                        * Starting with BUFSIZ if the application starts with
+                        * an empty buffer.
+                        */
+                       if (*n == 0)
+                               *n = BUFSIZ;
+
+                       if ((newp = realloc(*lineptr, *n)) == NULL) {
+                               FUNLOCKFILE(fp);
+                               free(*lineptr);
+                               *lineptr = NULL;
+                               *n = 0;
+                               errno = ENOMEM;
+                               return -1;
+                       }
+                       *lineptr = newp;
+               }
+       
+               memcpy((void*) (*lineptr + offset), (void*)fp->_p, len);
+               offset += len;
+               fp->_r -= len;
+               fp->_p += len;
+       } while (c == NULL && len != 0);
+       FUNLOCKFILE(fp);
+
+       *(*lineptr + offset) = '\0';
+
+       /* end of file */
+       if (offset == 0)
+               return -1;
+
+       return (ssize_t) offset;
+}
+
+ssize_t
+getline(char** lineptr, size_t* n, FILE* fp)
+{
+       return getdelim(lineptr, n, '\n', fp);
+}

Reply via email to