]> CyberLeo.Net >> Repos - FreeBSD/FreeBSD.git/blob - usr.bin/grep/file.c
ident(1): Normalizing date format
[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         char *ret;
102         size_t len;
103         size_t off;
104         ptrdiff_t diff;
105
106         /* Fill the buffer, if necessary */
107         if (bufrem == 0 && grep_refill(f) != 0)
108                 goto error;
109
110         if (bufrem == 0) {
111                 /* Return zero length to indicate EOF */
112                 pc->ln.len= 0;
113                 return (bufpos);
114         }
115
116         /* Look for a newline in the remaining part of the buffer */
117         if ((p = memchr(bufpos, fileeol, bufrem)) != NULL) {
118                 ++p; /* advance over newline */
119                 ret = bufpos;
120                 len = p - bufpos;
121                 bufrem -= len;
122                 bufpos = p;
123                 pc->ln.len = len;
124                 return (ret);
125         }
126
127         /* We have to copy the current buffered data to the line buffer */
128         for (len = bufrem, off = 0; ; len += bufrem) {
129                 /* Make sure there is room for more data */
130                 if (grep_lnbufgrow(len + LNBUFBUMP))
131                         goto error;
132                 memcpy(lnbuf + off, bufpos, len - off);
133                 /* With FILE_MMAP, this is EOF; there's no more to refill */
134                 if (filebehave == FILE_MMAP) {
135                         bufrem -= len;
136                         break;
137                 }
138                 off = len;
139                 /* Fetch more to try and find EOL/EOF */
140                 if (grep_refill(f) != 0)
141                         goto error;
142                 if (bufrem == 0)
143                         /* EOF: return partial line */
144                         break;
145                 if ((p = memchr(bufpos, fileeol, bufrem)) == NULL)
146                         continue;
147                 /* got it: finish up the line (like code above) */
148                 ++p;
149                 diff = p - bufpos;
150                 len += diff;
151                 if (grep_lnbufgrow(len))
152                     goto error;
153                 memcpy(lnbuf + off, bufpos, diff);
154                 bufrem -= diff;
155                 bufpos = p;
156                 break;
157         }
158         pc->ln.len = len;
159         return (lnbuf);
160
161 error:
162         pc->ln.len = 0;
163         return (NULL);
164 }
165
166 /*
167  * Opens a file for processing.
168  */
169 struct file *
170 grep_open(const char *path)
171 {
172         struct file *f;
173
174         f = grep_malloc(sizeof *f);
175         memset(f, 0, sizeof *f);
176         if (path == NULL) {
177                 /* Processing stdin implies --line-buffered. */
178                 lbflag = true;
179                 f->fd = STDIN_FILENO;
180         } else if ((f->fd = open(path, O_RDONLY)) == -1)
181                 goto error1;
182
183         if (filebehave == FILE_MMAP) {
184                 struct stat st;
185
186                 if ((fstat(f->fd, &st) == -1) || (st.st_size > OFF_MAX) ||
187                     (!S_ISREG(st.st_mode)))
188                         filebehave = FILE_STDIO;
189                 else {
190                         int flags = MAP_PRIVATE | MAP_NOCORE | MAP_NOSYNC;
191 #ifdef MAP_PREFAULT_READ
192                         flags |= MAP_PREFAULT_READ;
193 #endif
194                         fsiz = st.st_size;
195                         buffer = mmap(NULL, fsiz, PROT_READ, flags,
196                              f->fd, (off_t)0);
197                         if (buffer == MAP_FAILED)
198                                 filebehave = FILE_STDIO;
199                         else {
200                                 bufrem = st.st_size;
201                                 bufpos = buffer;
202                                 madvise(buffer, st.st_size, MADV_SEQUENTIAL);
203                         }
204                 }
205         }
206
207         if ((buffer == NULL) || (buffer == MAP_FAILED))
208                 buffer = grep_malloc(MAXBUFSIZ);
209
210         /* Fill read buffer, also catches errors early */
211         if (bufrem == 0 && grep_refill(f) != 0)
212                 goto error2;
213
214         /* Check for binary stuff, if necessary */
215         if (binbehave != BINFILE_TEXT && fileeol != '\0' &&
216             memchr(bufpos, '\0', bufrem) != NULL)
217                 f->binary = true;
218
219         return (f);
220
221 error2:
222         close(f->fd);
223 error1:
224         free(f);
225         return (NULL);
226 }
227
228 /*
229  * Closes a file.
230  */
231 void
232 grep_close(struct file *f)
233 {
234
235         close(f->fd);
236
237         /* Reset read buffer and line buffer */
238         if (filebehave == FILE_MMAP) {
239                 munmap(buffer, fsiz);
240                 buffer = NULL;
241         }
242         bufpos = buffer;
243         bufrem = 0;
244
245         free(lnbuf);
246         lnbuf = NULL;
247         lnbuflen = 0;
248 }