|
20 | 20 | #include "postgres_fe.h"
|
21 | 21 | #include "libpq-fe.h"
|
22 | 22 |
|
| 23 | +#include <fcntl.h> |
23 | 24 | #include <locale.h>
|
24 | 25 | #include <signal.h>
|
25 | 26 | #include <sys/types.h>
|
@@ -297,50 +298,77 @@ get_pgpid(void)
|
297 | 298 | static char **
|
298 | 299 | readfile(const char *path)
|
299 | 300 | {
|
300 |
| - FILE *infile; |
301 |
| - int maxlength = 1, |
302 |
| - linelen = 0; |
303 |
| - int nlines = 0; |
| 301 | + int fd; |
| 302 | + int nlines; |
304 | 303 | char **result;
|
305 | 304 | char *buffer;
|
306 |
| - int c; |
| 305 | + char *linebegin; |
| 306 | + int i; |
| 307 | + int n; |
| 308 | + int len; |
| 309 | + struct stat statbuf; |
307 | 310 |
|
308 |
| - if ((infile = fopen(path, "r")) == NULL) |
| 311 | + /* |
| 312 | + * Slurp the file into memory. |
| 313 | + * |
| 314 | + * The file can change concurrently, so we read the whole file into memory |
| 315 | + * with a single read() call. That's not guaranteed to get an atomic |
| 316 | + * snapshot, but in practice, for a small file, it's close enough for the |
| 317 | + * current use. |
| 318 | + */ |
| 319 | + fd = open(path, O_RDONLY | PG_BINARY, 0); |
| 320 | + if (fd < 0) |
309 | 321 | return NULL;
|
| 322 | + if (fstat(fd, &statbuf) < 0) |
| 323 | + return NULL; |
| 324 | + if (statbuf.st_size == 0) |
| 325 | + { |
| 326 | + /* empty file */ |
| 327 | + result = (char **) pg_malloc(sizeof(char *)); |
| 328 | + *result = NULL; |
| 329 | + return result; |
| 330 | + } |
| 331 | + buffer = pg_malloc(statbuf.st_size + 1); |
310 | 332 |
|
311 |
| - /* pass over the file twice - the first time to size the result */ |
| 333 | + len = read(fd, buffer, statbuf.st_size + 1); |
| 334 | + close(fd); |
| 335 | + if (len != statbuf.st_size) |
| 336 | + { |
| 337 | + /* oops, the file size changed between fstat and read */ |
| 338 | + free(buffer); |
| 339 | + return NULL; |
| 340 | + } |
312 | 341 |
|
313 |
| - while ((c = fgetc(infile)) != EOF) |
| 342 | + /* count newlines */ |
| 343 | + nlines = 0; |
| 344 | + for (i = 0; i < len - 1; i++) |
314 | 345 | {
|
315 |
| - linelen++; |
316 |
| - if (c == '\n') |
317 |
| - { |
| 346 | + if (buffer[i] == '\n') |
318 | 347 | nlines++;
|
319 |
| - if (linelen > maxlength) |
320 |
| - maxlength = linelen; |
321 |
| - linelen = 0; |
322 |
| - } |
323 | 348 | }
|
| 349 | + nlines++; /* account for the last line */ |
324 | 350 |
|
325 |
| - /* handle last line without a terminating newline (yuck) */ |
326 |
| - if (linelen) |
327 |
| - nlines++; |
328 |
| - if (linelen > maxlength) |
329 |
| - maxlength = linelen; |
330 |
| - |
331 |
| - /* set up the result and the line buffer */ |
| 351 | + /* set up the result buffer */ |
332 | 352 | result = (char **) pg_malloc((nlines + 1) * sizeof(char *));
|
333 |
| - buffer = (char *) pg_malloc(maxlength + 1); |
334 | 353 |
|
335 |
| - /* now reprocess the file and store the lines */ |
336 |
| - rewind(infile); |
337 |
| - nlines = 0; |
338 |
| - while (fgets(buffer, maxlength + 1, infile) != NULL) |
339 |
| - result[nlines++] = xstrdup(buffer); |
| 354 | + /* now split the buffer into lines */ |
| 355 | + linebegin = buffer; |
| 356 | + n = 0; |
| 357 | + for (i = 0; i < len; i++) |
| 358 | + { |
| 359 | + if (buffer[i] == '\n' || i == len - 1) |
| 360 | + { |
| 361 | + int slen = &buffer[i] - linebegin + 1; |
| 362 | + char *linebuf = pg_malloc(slen + 1); |
| 363 | + memcpy(linebuf, linebegin, slen); |
| 364 | + linebuf[slen] = '\0'; |
| 365 | + result[n++] = linebuf; |
| 366 | + linebegin = &buffer[i + 1]; |
| 367 | + } |
| 368 | + } |
| 369 | + result[n] = NULL; |
340 | 370 |
|
341 |
| - fclose(infile); |
342 | 371 | free(buffer);
|
343 |
| - result[nlines] = NULL; |
344 | 372 |
|
345 | 373 | return result;
|
346 | 374 | }
|
|
0 commit comments