|
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 <time.h> |
@@ -315,50 +316,77 @@ get_pgpid(void) |
315 | 316 | static char ** |
316 | 317 | readfile(const char *path) |
317 | 318 | { |
318 | | - FILE *infile; |
319 | | - int maxlength = 1, |
320 | | - linelen = 0; |
321 | | - int nlines = 0; |
| 319 | + int fd; |
| 320 | + int nlines; |
322 | 321 | char **result; |
323 | 322 | char *buffer; |
324 | | - int c; |
| 323 | + char *linebegin; |
| 324 | + int i; |
| 325 | + int n; |
| 326 | + int len; |
| 327 | + struct stat statbuf; |
325 | 328 |
|
326 | | - if ((infile = fopen(path, "r")) == NULL) |
| 329 | + /* |
| 330 | + * Slurp the file into memory. |
| 331 | + * |
| 332 | + * The file can change concurrently, so we read the whole file into memory |
| 333 | + * with a single read() call. That's not guaranteed to get an atomic |
| 334 | + * snapshot, but in practice, for a small file, it's close enough for the |
| 335 | + * current use. |
| 336 | + */ |
| 337 | + fd = open(path, O_RDONLY | PG_BINARY, 0); |
| 338 | + if (fd < 0) |
327 | 339 | return NULL; |
| 340 | + if (fstat(fd, &statbuf) < 0) |
| 341 | + return NULL; |
| 342 | + if (statbuf.st_size == 0) |
| 343 | + { |
| 344 | + /* empty file */ |
| 345 | + result = (char **) pg_malloc(sizeof(char *)); |
| 346 | + *result = NULL; |
| 347 | + return result; |
| 348 | + } |
| 349 | + buffer = pg_malloc(statbuf.st_size + 1); |
328 | 350 |
|
329 | | - /* pass over the file twice - the first time to size the result */ |
| 351 | + len = read(fd, buffer, statbuf.st_size + 1); |
| 352 | + close(fd); |
| 353 | + if (len != statbuf.st_size) |
| 354 | + { |
| 355 | + /* oops, the file size changed between fstat and read */ |
| 356 | + free(buffer); |
| 357 | + return NULL; |
| 358 | + } |
330 | 359 |
|
331 | | - while ((c = fgetc(infile)) != EOF) |
| 360 | + /* count newlines */ |
| 361 | + nlines = 0; |
| 362 | + for (i = 0; i < len - 1; i++) |
332 | 363 | { |
333 | | - linelen++; |
334 | | - if (c == '\n') |
335 | | - { |
| 364 | + if (buffer[i] == '\n') |
336 | 365 | nlines++; |
337 | | - if (linelen > maxlength) |
338 | | - maxlength = linelen; |
339 | | - linelen = 0; |
340 | | - } |
341 | 366 | } |
| 367 | + nlines++; /* account for the last line */ |
342 | 368 |
|
343 | | - /* handle last line without a terminating newline (yuck) */ |
344 | | - if (linelen) |
345 | | - nlines++; |
346 | | - if (linelen > maxlength) |
347 | | - maxlength = linelen; |
348 | | - |
349 | | - /* set up the result and the line buffer */ |
| 369 | + /* set up the result buffer */ |
350 | 370 | result = (char **) pg_malloc((nlines + 1) * sizeof(char *)); |
351 | | - buffer = (char *) pg_malloc(maxlength + 1); |
352 | 371 |
|
353 | | - /* now reprocess the file and store the lines */ |
354 | | - rewind(infile); |
355 | | - nlines = 0; |
356 | | - while (fgets(buffer, maxlength + 1, infile) != NULL) |
357 | | - result[nlines++] = pg_strdup(buffer); |
| 372 | + /* now split the buffer into lines */ |
| 373 | + linebegin = buffer; |
| 374 | + n = 0; |
| 375 | + for (i = 0; i < len; i++) |
| 376 | + { |
| 377 | + if (buffer[i] == '\n' || i == len - 1) |
| 378 | + { |
| 379 | + int slen = &buffer[i] - linebegin + 1; |
| 380 | + char *linebuf = pg_malloc(slen + 1); |
| 381 | + memcpy(linebuf, linebegin, slen); |
| 382 | + linebuf[slen] = '\0'; |
| 383 | + result[n++] = linebuf; |
| 384 | + linebegin = &buffer[i + 1]; |
| 385 | + } |
| 386 | + } |
| 387 | + result[n] = NULL; |
358 | 388 |
|
359 | | - fclose(infile); |
360 | 389 | free(buffer); |
361 | | - result[nlines] = NULL; |
362 | 390 |
|
363 | 391 | return result; |
364 | 392 | } |
|
0 commit comments