hush: fix read builtin to not read ahead past eol and to not use
[oweals/busybox.git] / libbb / read.c
1 /* vi: set sw=4 ts=4: */
2 /*
3  * Utility routines.
4  *
5  * Copyright (C) 1999-2004 by Erik Andersen <andersen@codepoet.org>
6  *
7  * Licensed under GPLv2 or later, see file LICENSE in this tarball for details.
8  */
9
10 #include "libbb.h"
11
12 ssize_t safe_read(int fd, void *buf, size_t count)
13 {
14         ssize_t n;
15
16         do {
17                 n = read(fd, buf, count);
18         } while (n < 0 && errno == EINTR);
19
20         return n;
21 }
22
23 /*
24  * Read all of the supplied buffer from a file.
25  * This does multiple reads as necessary.
26  * Returns the amount read, or -1 on an error.
27  * A short read is returned on an end of file.
28  */
29 ssize_t full_read(int fd, void *buf, size_t len)
30 {
31         ssize_t cc;
32         ssize_t total;
33
34         total = 0;
35
36         while (len) {
37                 cc = safe_read(fd, buf, len);
38
39                 if (cc < 0)
40                         return cc;      /* read() returns -1 on failure. */
41                 if (cc == 0)
42                         break;
43                 buf = ((char *)buf) + cc;
44                 total += cc;
45                 len -= cc;
46         }
47
48         return total;
49 }
50
51 // Die with an error message if we can't read the entire buffer.
52 void xread(int fd, void *buf, size_t count)
53 {
54         if (count) {
55                 ssize_t size = full_read(fd, buf, count);
56                 if (size != count)
57                         bb_error_msg_and_die("short read");
58         }
59 }
60
61 // Die with an error message if we can't read one character.
62 unsigned char xread_char(int fd)
63 {
64         char tmp;
65         xread(fd, &tmp, 1);
66         return tmp;
67 }
68
69 // Read one line a-la fgets. Works only on seekable streams
70 char *reads(int fd, char *buffer, size_t size)
71 {
72         char *p;
73
74         if (size < 2)
75                 return NULL;
76         size = full_read(fd, buffer, size-1);
77         if ((ssize_t)size <= 0)
78                 return NULL;
79
80         buffer[size] = '\0';
81         p = strchr(buffer, '\n');
82         if (p) {
83                 off_t offset;
84                 *p++ = '\0';
85                 // avoid incorrect (unsigned) widening
86                 offset = (off_t)(p-buffer) - (off_t)size;
87                 // set fd position right after '\n'
88                 if (offset && lseek(fd, offset, SEEK_CUR) == (off_t)-1)
89                         return NULL;
90         }
91         return buffer;
92 }
93
94 // Read one line a-la fgets. Reads byte-by-byte.
95 // Useful when it is important to not read ahead.
96 char *xmalloc_reads(int fd, char *buf)
97 {
98         char *p;
99         int sz = buf ? strlen(buf) : 0;
100
101         goto jump_in;
102         while (1) {
103                 if (p - buf == sz) {
104  jump_in:
105                         buf = xrealloc(buf, sz + 128);
106                         p = buf + sz;
107                         sz += 128;
108                 }
109                 if (safe_read(fd, p, 1) != 1) { /* EOF/error */
110                         if (p == buf) {
111                                 /* we read nothing [and buf was NULL initially] */
112                                 free(buf);
113                                 return NULL;
114                         }
115                         break;
116                 }
117                 if (*p == '\n')
118                         break;
119                 p++;
120         }
121         *p++ = '\0';
122         return xrealloc(buf, p - buf);
123 }
124
125 ssize_t read_close(int fd, void *buf, size_t size)
126 {
127         int e;
128         size = full_read(fd, buf, size);
129         e = errno;
130         close(fd);
131         errno = e;
132         return size;
133 }
134
135 ssize_t open_read_close(const char *filename, void *buf, size_t size)
136 {
137         int fd = open(filename, O_RDONLY);
138         if (fd < 0)
139                 return fd;
140         return read_close(fd, buf, size);
141 }
142
143 // Read (potentially big) files in one go. File size is estimated by
144 // lseek to end.
145 void *xmalloc_open_read_close(const char *filename, size_t *sizep)
146 {
147         char *buf;
148         size_t size = sizep ? *sizep : INT_MAX;
149         int fd;
150         off_t len;
151
152         fd = xopen(filename, O_RDONLY);
153         /* /proc/N/stat files report len 0 here */
154         /* In order to make such files readable, we add small const */
155         len = xlseek(fd, 0, SEEK_END) | 0x3ff; /* + up to 1k */
156         xlseek(fd, 0, SEEK_SET);
157         if (len < size)
158                 size = len;
159         buf = xmalloc(size + 1);
160         size = read_close(fd, buf, size);
161         if ((ssize_t)size < 0)
162                 bb_perror_msg_and_die("'%s'", filename);
163         xrealloc(buf, size + 1);
164         buf[size] = '\0';
165         if (sizep)
166                 *sizep = size;
167         return buf;
168 }