lib: Optimize file_compare
[samba.git] / lib / util / util_file.c
1 /*
2  * Unix SMB/CIFS implementation.
3  * SMB parameters and setup
4  * Copyright (C) Andrew Tridgell 1992-1998 Modified by Jeremy Allison 1995.
5  *
6  * Added afdgets() Jelmer Vernooij 2005
7  *
8  * This program is free software; you can redistribute it and/or modify it under
9  * the terms of the GNU General Public License as published by the Free
10  * Software Foundation; either version 3 of the License, or (at your option)
11  * any later version.
12  *
13  * This program is distributed in the hope that it will be useful, but WITHOUT
14  * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
15  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
16  * more details.
17  *
18  * You should have received a copy of the GNU General Public License along with
19  * this program; if not, see <http://www.gnu.org/licenses/>.
20  */
21
22 #include "replace.h"
23 #include "system/shmem.h"
24 #include "system/filesys.h"
25 #include <talloc.h>
26 #include "lib/util/samba_util.h"
27 #include "lib/util/sys_popen.h"
28 #include "lib/util/sys_rw.h"
29 #include "lib/util/debug.h"
30
31 /**
32  * Read one line (data until next newline or eof) and allocate it
33  */
34 _PUBLIC_ char *afdgets(int fd, TALLOC_CTX *mem_ctx, size_t hint)
35 {
36         char *data = NULL;
37         ssize_t alloc_size = 0, offset = 0, ret;
38         int p;
39
40         if (hint <= 0) hint = 0x100;
41
42         do {
43                 alloc_size += hint;
44
45                 data = talloc_realloc(mem_ctx, data, char, alloc_size);
46
47                 if (!data)
48                         return NULL;
49
50                 ret = read(fd, data + offset, hint);
51
52                 if (ret == 0) {
53                         return NULL;
54                 }
55
56                 if (ret == -1) {
57                         talloc_free(data);
58                         return NULL;
59                 }
60
61                 /* Find newline */
62                 for (p = 0; p < ret; p++) {
63                         if (data[offset + p] == '\n')
64                                 break;
65                 }
66
67                 if (p < ret) {
68                         data[offset + p] = '\0';
69
70                         /* Go back to position of newline */
71                         lseek(fd, p - ret + 1, SEEK_CUR);
72                         return data;
73                 }
74
75                 offset += ret;
76
77         } while ((size_t)ret == hint);
78
79         data[offset] = '\0';
80
81         return data;
82 }
83
84 char *fgets_slash(TALLOC_CTX *mem_ctx, char *s2, size_t maxlen, FILE *f)
85 {
86         char *s = s2;
87         size_t len = 0;
88         int c;
89         bool start_of_line = true;
90
91         if (feof(f)) {
92                 return NULL;
93         }
94
95         if (maxlen < 2) {
96                 return NULL;
97         }
98
99         if (s2 == NULL) {
100                 maxlen = MIN(maxlen,8);
101                 s = talloc_array(mem_ctx, char, maxlen);
102         }
103
104         if (s == NULL) {
105                 return NULL;
106         }
107
108         *s = 0;
109
110         while (len < maxlen-1) {
111                 c = getc(f);
112                 switch (c)
113                 {
114                     case '\r':
115                             break;
116                     case '\n':
117                             while (len > 0 && s[len-1] == ' ') {
118                                     s[--len] = 0;
119                             }
120                             if (len > 0 && s[len-1] == '\\') {
121                                     s[--len] = 0;
122                                     start_of_line = true;
123                                     break;
124                             }
125                             return s;
126                     case EOF:
127                             if (len <= 0 && (s2 == NULL)) {
128                                     TALLOC_FREE(s);
129                             }
130                             return (len>0) ? s : NULL;
131                     case ' ':
132                             if (start_of_line) {
133                                     break;
134                             }
135
136                             FALL_THROUGH;
137                     default:
138                             start_of_line = false;
139                             s[len++] = c;
140                             s[len] = 0;
141                 }
142                 if ((s2 == NULL) && (len > maxlen-3)) {
143                         size_t m;
144                         char *t;
145
146                         m = maxlen * 2;
147                         if (m < maxlen) {
148                                 DBG_ERR("length overflow");
149                                 TALLOC_FREE(s);
150                                 return NULL;
151                         }
152                         maxlen = m;
153
154                         t = talloc_realloc(mem_ctx, s, char, maxlen);
155                         if (t == NULL) {
156                                 DBG_ERR("failed to expand buffer!\n");
157                                 TALLOC_FREE(s);
158                                 return NULL;
159                         }
160
161                         s = t;
162                 }
163         }
164
165         return s;
166 }
167
168 /**
169 load a file into memory from a fd.
170 **/
171 _PUBLIC_ char *fd_load(int fd, size_t *psize, size_t maxsize, TALLOC_CTX *mem_ctx)
172 {
173         FILE *file;
174         char *p = NULL;
175         size_t size = 0;
176         size_t chunk = 1024;
177         int err;
178
179         if (maxsize == 0) {
180                 maxsize = SIZE_MAX;
181         }
182
183         file = fdopen(fd, "r");
184         if (file == NULL) {
185                 return NULL;
186         }
187
188         while (size < maxsize) {
189                 size_t newbufsize;
190                 size_t nread;
191
192                 chunk = MIN(chunk, (maxsize - size));
193
194                 newbufsize = size + (chunk+1); /* chunk+1 can't overflow */
195                 if (newbufsize < size) {
196                         goto fail; /* overflow */
197                 }
198
199                 p = talloc_realloc(mem_ctx, p, char, newbufsize);
200                 if (p == NULL) {
201                         goto fail;
202                 }
203
204                 nread = fread(p+size, 1, chunk, file);
205                 size += nread;
206
207                 if (nread != chunk) {
208                         break;
209                 }
210         }
211
212         err = ferror(file);
213         if (err != 0) {
214                 goto fail;
215         }
216
217         p[size] = '\0';
218
219         if (psize != NULL) {
220                 *psize = size;
221         }
222
223         fclose(file);
224         return p;
225
226 fail:
227         TALLOC_FREE(p);
228         fclose(file);
229         return NULL;
230 }
231
232 /**
233 load a file into memory
234 **/
235 _PUBLIC_ char *file_load(const char *fname, size_t *size, size_t maxsize, TALLOC_CTX *mem_ctx)
236 {
237         int fd;
238         char *p;
239
240         if (!fname || !*fname) return NULL;
241
242         fd = open(fname,O_RDONLY);
243         if (fd == -1) return NULL;
244
245         p = fd_load(fd, size, maxsize, mem_ctx);
246
247         close(fd);
248
249         return p;
250 }
251
252 /**
253 parse a buffer into lines
254 'p' will be freed on error, and otherwise will be made a child of the returned array
255 **/
256 char **file_lines_parse(char *p, size_t size, int *numlines, TALLOC_CTX *mem_ctx)
257 {
258         unsigned int i;
259         char *s, **ret;
260
261         if (!p) return NULL;
262
263         for (s = p, i=0; s < p+size; s++) {
264                 if (s[0] == '\n') i++;
265         }
266
267         ret = talloc_zero_array(mem_ctx, char *, i+2);
268         if (!ret) {
269                 talloc_free(p);
270                 return NULL;
271         }
272
273         talloc_steal(ret, p);
274
275         ret[0] = p;
276         for (s = p, i=1; s < p+size; s++) {
277                 if (s[0] == '\n') {
278                         s[0] = 0;
279                         ret[i] = s+1;
280                         i++;
281                 }
282                 if (s[0] == '\r') s[0] = 0;
283         }
284
285         /* remove any blank lines at the end */
286         while (i > 0 && ret[i-1][0] == 0) {
287                 i--;
288         }
289
290         if (numlines) *numlines = i;
291
292         return ret;
293 }
294
295
296 /**
297 load a file into memory and return an array of pointers to lines in the file
298 must be freed with talloc_free().
299 **/
300 _PUBLIC_ char **file_lines_load(const char *fname, int *numlines, size_t maxsize, TALLOC_CTX *mem_ctx)
301 {
302         char *p;
303         size_t size;
304
305         p = file_load(fname, &size, maxsize, mem_ctx);
306         if (!p) return NULL;
307
308         return file_lines_parse(p, size, numlines, mem_ctx);
309 }
310
311 /**
312 load a fd into memory and return an array of pointers to lines in the file
313 must be freed with talloc_free(). If convert is true calls unix_to_dos on
314 the list.
315 **/
316 _PUBLIC_ char **fd_lines_load(int fd, int *numlines, size_t maxsize, TALLOC_CTX *mem_ctx)
317 {
318         char *p;
319         size_t size;
320
321         p = fd_load(fd, &size, maxsize, mem_ctx);
322         if (!p) return NULL;
323
324         return file_lines_parse(p, size, numlines, mem_ctx);
325 }
326
327 _PUBLIC_ bool file_save_mode(const char *fname, const void *packet,
328                              size_t length, mode_t mode)
329 {
330         ssize_t num_written;
331         int fd;
332         fd = open(fname, O_WRONLY|O_CREAT|O_TRUNC, mode);
333         if (fd == -1) {
334                 return false;
335         }
336         num_written = write(fd, packet, length);
337         if (num_written == -1 || (size_t)num_written != length) {
338                 close(fd);
339                 return false;
340         }
341         close(fd);
342         return true;
343 }
344
345 /**
346   save a lump of data into a file. Mostly used for debugging
347 */
348 _PUBLIC_ bool file_save(const char *fname, const void *packet, size_t length)
349 {
350         return file_save_mode(fname, packet, length, 0644);
351 }
352
353 _PUBLIC_ int vfdprintf(int fd, const char *format, va_list ap)
354 {
355         char *p;
356         int len, ret;
357         va_list ap2;
358
359         va_copy(ap2, ap);
360         len = vasprintf(&p, format, ap2);
361         va_end(ap2);
362         if (len <= 0) return len;
363         ret = write(fd, p, len);
364         SAFE_FREE(p);
365         return ret;
366 }
367
368 _PUBLIC_ int fdprintf(int fd, const char *format, ...)
369 {
370         va_list ap;
371         int ret;
372
373         va_start(ap, format);
374         ret = vfdprintf(fd, format, ap);
375         va_end(ap);
376         return ret;
377 }
378
379
380 /*
381   compare two files, return true if the two files have the same content
382  */
383 bool file_compare(const char *path1, const char *path2)
384 {
385         FILE *f1 = NULL, *f2 = NULL;
386         uint8_t buf1[1024], buf2[1024];
387         bool ret = false;
388
389         f1 = fopen(path1, "r");
390         if (f1 == NULL) {
391                 goto done;
392         }
393         f2 = fopen(path2, "r");
394         if (f2 == NULL) {
395                 goto done;
396         }
397
398         while (!feof(f1)) {
399                 size_t n1 = fread(buf1, 1, sizeof(buf1), f1);
400                 size_t n2 = fread(buf2, 1, sizeof(buf2), f2);
401
402                 if (n1 != n2) {
403                         goto done;
404                 }
405                 if (n1 == 0) {
406                         ret = (feof(f1) && feof(f2));
407                         goto done;
408                 }
409                 if (memcmp(buf1, buf2, n1) != 0) {
410                         goto done;
411                 }
412                 if (n1 < sizeof(buf1)) {
413                         bool has_error = (ferror(f1) || ferror(f2));
414                         if (has_error) {
415                                 goto done;
416                         }
417                 }
418         }
419         ret = true;
420 done:
421         if (f2 != NULL) {
422                 fclose(f2);
423         }
424         if (f1 != NULL) {
425                 fclose(f1);
426         }
427         return ret;
428 }
429
430 /**
431  Load from a pipe into memory.
432 **/
433 char *file_ploadv(char * const argl[], size_t *size)
434 {
435         int fd, n;
436         char *p = NULL;
437         char buf[1024];
438         size_t total;
439
440         fd = sys_popenv(argl);
441         if (fd == -1) {
442                 return NULL;
443         }
444
445         total = 0;
446
447         while ((n = sys_read(fd, buf, sizeof(buf))) > 0) {
448                 p = talloc_realloc(NULL, p, char, total + n + 1);
449                 if (p == NULL) {
450                         DBG_ERR("failed to expand buffer!\n");
451                         close(fd);
452                         return NULL;
453                 }
454                 memcpy(p+total, buf, n);
455                 total += n;
456         }
457
458         if (p != NULL) {
459                 p[total] = 0;
460         }
461
462         /*
463          * FIXME: Perhaps ought to check that the command completed
464          * successfully (returned 0); if not the data may be
465          * truncated.
466          */
467         sys_pclose(fd);
468
469         if (size) {
470                 *size = total;
471         }
472
473         return p;
474 }