summaryrefslogtreecommitdiff
path: root/src/pv/file.c
diff options
context:
space:
mode:
Diffstat (limited to 'src/pv/file.c')
-rw-r--r--src/pv/file.c250
1 files changed, 250 insertions, 0 deletions
diff --git a/src/pv/file.c b/src/pv/file.c
new file mode 100644
index 0000000..4eadec2
--- /dev/null
+++ b/src/pv/file.c
@@ -0,0 +1,250 @@
+/*
+ * Functions for opening and closing files.
+ *
+ * Copyright 2012 Andrew Wood, distributed under the Artistic License 2.0.
+ */
+
+#define _GNU_SOURCE 1
+#include <limits.h>
+
+#include <stdio.h>
+#include "options.h"
+
+#include <stdlib.h>
+#include <string.h>
+#include <errno.h>
+#include <sys/time.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <unistd.h>
+#include <fcntl.h>
+
+#ifdef HAVE_CONFIG_H
+#include "config.h"
+#endif
+
+/*
+ * Try to work out the total size of all data by adding up the sizes of all
+ * input files. If any of the input files are of indeterminate size (i.e.
+ * they are a pipe), the total size is set to zero.
+ *
+ * Any files that cannot be stat()ed or that access() says we can't read
+ * will cause a warning to be output and will be removed from the list.
+ *
+ * In line mode, any files that pass the above checks will then be read to
+ * determine how many lines they contain, and the total size will be set to
+ * the total line count. Only regular files will be read.
+ */
+void pv_calc_total_size(opts_t opts)
+{
+ struct stat64 sb;
+ int rc, i, j, fd;
+
+ opts->size = 0;
+ rc = 0;
+
+ if (opts->argc < 1) {
+ if (fstat64(STDIN_FILENO, &sb) == 0)
+ opts->size = sb.st_size;
+ return;
+ }
+
+ for (i = 0; i < opts->argc; i++) {
+ if (strcmp(opts->argv[i], "-") == 0) {
+ rc = fstat64(STDIN_FILENO, &sb);
+ if (rc != 0) {
+ opts->size = 0;
+ return;
+ }
+ } else {
+ rc = stat64(opts->argv[i], &sb);
+ if (rc == 0)
+ rc = access(opts->argv[i], R_OK);
+ }
+
+ if (rc != 0) {
+ fprintf(stderr, "%s: %s: %s\n", opts->program_name,
+ opts->argv[i], strerror(errno));
+ for (j = i; j < opts->argc - 1; j++) {
+ opts->argv[j] = opts->argv[j + 1];
+ }
+ opts->argc--;
+ i--;
+ opts->exit_status |= 2;
+ continue;
+ }
+
+ if (S_ISBLK(sb.st_mode)) {
+ /*
+ * Get the size of block devices by opening
+ * them and seeking to the end.
+ */
+ if (strcmp(opts->argv[i], "-") == 0) {
+ fd = open64("/dev/stdin", O_RDONLY);
+ } else {
+ fd = open64(opts->argv[i], O_RDONLY);
+ }
+ if (fd >= 0) {
+ opts->size += lseek64(fd, 0, SEEK_END);
+ close(fd);
+ } else {
+ fprintf(stderr, "%s: %s: %s\n",
+ opts->program_name, opts->argv[i],
+ strerror(errno));
+ opts->exit_status |= 2;
+ }
+ } else if (S_ISREG(sb.st_mode)) {
+ opts->size += sb.st_size;
+ } else {
+ opts->size = 0;
+ }
+ }
+
+ if (!opts->linemode)
+ return;
+
+ opts->size = 0;
+
+ for (i = 0; i < opts->argc; i++) {
+ fd = -1;
+
+ if (strcmp(opts->argv[i], "-") == 0) {
+ rc = fstat64(STDIN_FILENO, &sb);
+ if ((rc != 0) || (!S_ISREG(sb.st_mode))) {
+ opts->size = 0;
+ return;
+ }
+ fd = dup(STDIN_FILENO);
+ } else {
+ rc = stat64(opts->argv[i], &sb);
+ if ((rc != 0) || (!S_ISREG(sb.st_mode))) {
+ opts->size = 0;
+ return;
+ }
+ fd = open64(opts->argv[i], O_RDONLY);
+ }
+
+ if (fd < 0) {
+ fprintf(stderr, "%s: %s: %s\n", opts->program_name,
+ opts->argv[i], strerror(errno));
+ opts->size = 0;
+ opts->exit_status |= 2;
+ return;
+ }
+
+ while (1) {
+ unsigned char scanbuf[1024]; /* RATS: ignore (OK) */
+ int numread, j;
+
+ numread = read(fd, /* RATS: ignore (OK) */ scanbuf,
+ sizeof(scanbuf));
+ if (numread < 0) {
+ fprintf(stderr, "%s: %s: %s\n",
+ opts->program_name, opts->argv[i],
+ strerror(errno));
+ opts->exit_status |= 2;
+ break;
+ } else if (numread == 0) {
+ break;
+ }
+ for (j = 0; j < numread; j++) {
+ if (scanbuf[j] == '\n')
+ opts->size++;
+ }
+ }
+
+ lseek64(fd, 0, SEEK_SET);
+ close(fd);
+ }
+}
+
+
+/*
+ * Close the given file descriptor and open the next one, whose number in
+ * the list is "filenum", returning the new file descriptor (or negative on
+ * error). It is an error if the next input file is the same as the file
+ * stdout is pointing to.
+ */
+int pv_next_file(opts_t opts, int filenum, int oldfd)
+{
+ struct stat64 isb;
+ struct stat64 osb;
+ int fd;
+
+ if (oldfd > 0) {
+ if (close(oldfd)) {
+ fprintf(stderr, "%s: %s: %s\n",
+ opts->program_name,
+ _("failed to close file"),
+ strerror(errno));
+ opts->exit_status |= 8;
+ return -1;
+ }
+ }
+
+ if (filenum >= opts->argc) {
+ opts->exit_status |= 8;
+ return -1;
+ }
+
+ if (filenum < 0) {
+ opts->exit_status |= 8;
+ return -1;
+ }
+
+ if (strcmp(opts->argv[filenum], "-") == 0) {
+ fd = STDIN_FILENO;
+ } else {
+ fd = open64(opts->argv[filenum], O_RDONLY);
+ if (fd < 0) {
+ fprintf(stderr, "%s: %s: %s: %s\n",
+ opts->program_name,
+ _("failed to read file"),
+ opts->argv[filenum], strerror(errno));
+ opts->exit_status |= 2;
+ return -1;
+ }
+ }
+
+ if (fstat64(fd, &isb)) {
+ fprintf(stderr, "%s: %s: %s: %s\n",
+ opts->program_name,
+ _("failed to stat file"),
+ opts->argv[filenum], strerror(errno));
+ close(fd);
+ opts->exit_status |= 2;
+ return -1;
+ }
+
+ if (fstat64(STDOUT_FILENO, &osb)) {
+ fprintf(stderr, "%s: %s: %s\n",
+ opts->program_name,
+ _("failed to stat output file"), strerror(errno));
+ close(fd);
+ opts->exit_status |= 2;
+ return -1;
+ }
+
+ /*
+ * Check that this new input file is not the same as stdout's
+ * destination. This restriction is ignored for anything other
+ * than a regular file or block device.
+ */
+ if (isb.st_dev != osb.st_dev)
+ return fd;
+ if (isb.st_ino != osb.st_ino)
+ return fd;
+ if (isatty(fd))
+ return fd;
+ if ((!S_ISREG(isb.st_mode)) && (!S_ISBLK(isb.st_mode)))
+ return fd;
+
+ fprintf(stderr, "%s: %s: %s\n",
+ opts->program_name,
+ _("input file is output file"), opts->argv[filenum]);
+ close(fd);
+ opts->exit_status |= 4;
+ return -1;
+}
+
+/* EOF */