]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.bin/grep/file.c
Hyper-V: vPCI: Prepopulate device bars
[FreeBSD/FreeBSD.git] / usr.bin / grep / file.c
1 /*      $NetBSD: file.c,v 1.5 2011/02/16 18:35:39 joerg Exp $   */
2 /*      $FreeBSD$       */
3 /*      $OpenBSD: file.c,v 1.11 2010/07/02 20:48:48 nicm Exp $  */
4
5 /*-
6  * SPDX-License-Identifier: BSD-2-Clause-FreeBSD
7  *
8  * Copyright (c) 1999 James Howard and Dag-Erling Coïdan Smørgrav
9  * Copyright (C) 2008-2010 Gabor Kovesdan <gabor@FreeBSD.org>
10  * Copyright (C) 2010 Dimitry Andric <dimitry@andric.com>
11  * All rights reserved.
12  *
13  * Redistribution and use in source and binary forms, with or without
14  * modification, are permitted provided that the following conditions
15  * are met:
16  * 1. Redistributions of source code must retain the above copyright
17  *    notice, this list of conditions and the following disclaimer.
18  * 2. Redistributions in binary form must reproduce the above copyright
19  *    notice, this list of conditions and the following disclaimer in the
20  *    documentation and/or other materials provided with the distribution.
21  *
22  * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
23  * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
24  * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
25  * ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
26  * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
27  * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
28  * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
29  * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
30  * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
31  * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
32  * SUCH DAMAGE.
33  */
34
35 #include <sys/cdefs.h>
36 __FBSDID("$FreeBSD$");
37
38 #include <sys/param.h>
39 #include <sys/mman.h>
40 #include <sys/stat.h>
41 #include <sys/types.h>
42
43 #include <err.h>
44 #include <errno.h>
45 #include <fcntl.h>
46 #include <stddef.h>
47 #include <stdlib.h>
48 #include <string.h>
49 #include <unistd.h>
50 #include <wchar.h>
51 #include <wctype.h>
52
53 #include "grep.h"
54
55 #define MAXBUFSIZ       (32 * 1024)
56 #define LNBUFBUMP       80
57
58 static char *buffer;
59 static char *bufpos;
60 static size_t bufrem;
61 static size_t fsiz;
62
63 static char *lnbuf;
64 static size_t lnbuflen;
65
66 static inline int
67 grep_refill(struct file *f)
68 {
69         ssize_t nr;
70
71         if (filebehave == FILE_MMAP)
72                 return (0);
73
74         bufpos = buffer;
75         bufrem = 0;
76
77         nr = read(f->fd, buffer, MAXBUFSIZ);
78         if (nr < 0)
79                 return (-1);
80
81         bufrem = nr;
82         return (0);
83 }
84
85 static inline int
86 grep_lnbufgrow(size_t newlen)
87 {
88
89         if (lnbuflen < newlen) {
90                 lnbuf = grep_realloc(lnbuf, newlen);
91                 lnbuflen = newlen;
92         }
93
94         return (0);
95 }
96
97 char *
98 grep_fgetln(struct file *f, struct parsec *pc)
99 {
100         char *p;
101         size_t len;
102         size_t off;
103         ptrdiff_t diff;
104
105         /* Fill the buffer, if necessary */
106         if (bufrem == 0 && grep_refill(f) != 0)
107                 goto error;
108
109         if (bufrem == 0) {
110                 /* Return zero length to indicate EOF */
111                 pc->ln.len= 0;
112                 return (bufpos);
113         }
114
115         /* Look for a newline in the remaining part of the buffer */
116         if ((p = memchr(bufpos, fileeol, bufrem)) != NULL) {
117                 ++p; /* advance over newline */
118                 len = p - bufpos;
119                 if (grep_lnbufgrow(len + 1))
120                         goto error;
121                 memcpy(lnbuf, bufpos, len);
122                 bufrem -= len;
123                 bufpos = p;
124                 pc->ln.len = len;
125                 lnbuf[len] = '\0';
126                 return (lnbuf);
127         }
128
129         /* We have to copy the current buffered data to the line buffer */
130         for (len = bufrem, off = 0; ; len += bufrem) {
131                 /* Make sure there is room for more data */
132                 if (grep_lnbufgrow(len + LNBUFBUMP))
133                         goto error;
134                 memcpy(lnbuf + off, bufpos, len - off);
135                 /* With FILE_MMAP, this is EOF; there's no more to refill */
136                 if (filebehave == FILE_MMAP) {
137                         bufrem -= len;
138                         break;
139                 }
140                 off = len;
141                 /* Fetch more to try and find EOL/EOF */
142                 if (grep_refill(f) != 0)
143                         goto error;
144                 if (bufrem == 0)
145                         /* EOF: return partial line */
146                         break;
147                 if ((p = memchr(bufpos, fileeol, bufrem)) == NULL)
148                         continue;
149                 /* got it: finish up the line (like code above) */
150                 ++p;
151                 diff = p - bufpos;
152                 len += diff;
153                 if (grep_lnbufgrow(len + 1))
154                     goto error;
155                 memcpy(lnbuf + off, bufpos, diff);
156                 bufrem -= diff;
157                 bufpos = p;
158                 break;
159         }
160         pc->ln.len = len;
161         lnbuf[len] = '\0';
162         return (lnbuf);
163
164 error:
165         pc->ln.len = 0;
166         return (NULL);
167 }
168
169 /*
170  * Opens a file for processing.
171  */
172 struct file *
173 grep_open(const char *path)
174 {
175         struct file *f;
176
177         f = grep_malloc(sizeof *f);
178         memset(f, 0, sizeof *f);
179         if (path == NULL) {
180                 /* Processing stdin implies --line-buffered. */
181                 lbflag = true;
182                 f->fd = STDIN_FILENO;
183         } else if ((f->fd = open(path, O_RDONLY)) == -1)
184                 goto error1;
185
186         if (filebehave == FILE_MMAP) {
187                 struct stat st;
188
189                 if ((fstat(f->fd, &st) == -1) || (st.st_size > OFF_MAX) ||
190                     (!S_ISREG(st.st_mode)))
191                         filebehave = FILE_STDIO;
192                 else {
193                         int flags = MAP_PRIVATE | MAP_NOCORE | MAP_NOSYNC;
194 #ifdef MAP_PREFAULT_READ
195                         flags |= MAP_PREFAULT_READ;
196 #endif
197                         fsiz = st.st_size;
198                         buffer = mmap(NULL, fsiz, PROT_READ, flags,
199                              f->fd, (off_t)0);
200                         if (buffer == MAP_FAILED)
201                                 filebehave = FILE_STDIO;
202                         else {
203                                 bufrem = st.st_size;
204                                 bufpos = buffer;
205                                 madvise(buffer, st.st_size, MADV_SEQUENTIAL);
206                         }
207                 }
208         }
209
210         if ((buffer == NULL) || (buffer == MAP_FAILED))
211                 buffer = grep_malloc(MAXBUFSIZ);
212
213         /* Fill read buffer, also catches errors early */
214         if (bufrem == 0 && grep_refill(f) != 0)
215                 goto error2;
216
217         /* Check for binary stuff, if necessary */
218         if (binbehave != BINFILE_TEXT && fileeol != '\0' &&
219             memchr(bufpos, '\0', bufrem) != NULL)
220                 f->binary = true;
221
222         return (f);
223
224 error2:
225         close(f->fd);
226 error1:
227         free(f);
228         return (NULL);
229 }
230
231 /*
232  * Closes a file.
233  */
234 void
235 grep_close(struct file *f)
236 {
237
238         close(f->fd);
239
240         /* Reset read buffer and line buffer */
241         if (filebehave == FILE_MMAP) {
242                 munmap(buffer, fsiz);
243                 buffer = NULL;
244         }
245         bufpos = buffer;
246         bufrem = 0;
247
248         free(lnbuf);
249         lnbuf = NULL;
250         lnbuflen = 0;
251 }