Repository: nanomsg/nng (c)

Issue:
nngcat should accept --data or --file over stdin
Note that by filing an issue, you agree that you have read and agreed to
our contribution guidelines.

## NNG & Platform details.

nng 1.1.1 (via FreeBSD ports)

## Expected Behavior

```
ping localhost | nngcat --pub --listen=tcp://127.0.0.1:4567 -i 1 -v --file -
```

## Actual Behavior

```
Protocol requires either --file or --data.
```

it seems nngcat doesn't accept piped stdin, nor via `--file -` or `--data -` or the same without `-`.

## Steps to Reproduce

See above, run `nngcat --sub --dial=tcp://127.0.0.1:4567 --raw` in another term.

Pull request description:
fixes #1007 nngcat should accept data from stdin
Allows passing a filename of `-` to the `--file` option in order to read from
stdin. Also removes the requirement for the file to be seekable - this allows
nngcat to be used as part of a shell pipeline, or with other non-seekable files

Patch:
diff --git a/docs/man/nng_cv_wait.3supp.adoc b/docs/man/nng_cv_wait.3supp.adoc
index 673839b9..69a21208 100644
--- a/docs/man/nng_cv_wait.3supp.adoc
+++ b/docs/man/nng_cv_wait.3supp.adoc
@@ -61,7 +61,7 @@ The following example demonstrates use of this function:
 ----
     nng_mtx_lock(m);
     condition_true = true;
-    cv_wake(cv);
+    nng_cv_wake(cv);
     nng_mtx_unlock(m);
 ----
 
diff --git a/docs/man/nngcat.1.adoc b/docs/man/nngcat.1.adoc
index f7850d34..fecc98c1 100644
--- a/docs/man/nngcat.1.adoc
+++ b/docs/man/nngcat.1.adoc
@@ -242,7 +242,7 @@ Protocols that support sending data can use these options to select the data.
   Use _DATA_ for the body of outgoing messages.
 
 *-F, --file*=_FILE_::
-  Use _FILE_ for the body of outgoing messages.
+  Use _FILE_ for the body of outgoing messages. If _FILE_ is _-_ the message body will be read from standard input.
 
 *-i, --interval*=_SEC_::
   For protocols that send unsolicited data (as opposed to those that
diff --git a/tools/nngcat/CMakeLists.txt b/tools/nngcat/CMakeLists.txt
index 6edf3bda..bcfc575b 100644
--- a/tools/nngcat/CMakeLists.txt
+++ b/tools/nngcat/CMakeLists.txt
@@ -29,5 +29,6 @@ if (NNG_ENABLE_NNGCAT)
     add_nngcat_test (nngcat_pubsub 20)
     add_nngcat_test (nngcat_recvmaxsz 20)
     add_nngcat_test (nngcat_unlimited 20)
+    add_nngcat_test (nngcat_stdin_pipe 20)
     endif()
 endif()
diff --git a/tools/nngcat/nngcat.c b/tools/nngcat/nngcat.c
index ab891830..7b5146c0 100644
--- a/tools/nngcat/nngcat.c
+++ b/tools/nngcat/nngcat.c
@@ -1,6 +1,7 @@
 //
 // Copyright 2019 Staysail Systems, Inc. <info@staysail.tech>
 // Copyright 2018 Capitar IT Group BV <info@capitar.com>
+// Copyright 2020 Lager Data, Inc. <support@lagerdata.com>
 //
 // This software is supplied under the terms of the MIT License, a
 // copy of which should be located in the distribution where this
@@ -272,7 +273,8 @@ help(void)
 	printf("  --key <file>\n");
 	printf("  --zt-home <path>\n");
 	printf("\n<src> may be one of:\n");
-	printf("  --file <file>          (or alias -F <file>)\n");
+	printf("  --file <file>          (or alias -F <file>). "
+	       "Use - for standard input.\n");
 	printf("  --data <data>          (or alias -D <data>)\n");
 	exit(1);
 }
@@ -309,28 +311,52 @@ static void
 loadfile(const char *path, void **datap, size_t *lenp)
 {
 	FILE * f;
+	size_t total_read = 0;
+	size_t allocation_size = BUFSIZ;
 	char * fdata;
-	size_t len;
+	char * realloc_result;
 
-	if ((f = fopen(path, "r")) == NULL) {
-		fatal("Cannot open file %s: %s", path, strerror(errno));
-	}
-	if (fseek(f, 0, SEEK_END) != 0) {
-		fatal("Cannot seek to end of file: %s", strerror(errno));
+	if (strcmp(path, "-") == 0) {
+		f = stdin;
+	} else {
+		if ((f = fopen(path, "rb")) == NULL) {
+			fatal("Cannot open file %s: %s", path, strerror(errno));
+		}
 	}
-	len = ftell(f);
-	(void) fseek(f, 0, SEEK_SET);
-	if ((fdata = malloc(len + 1)) == NULL) {
+
+	if ((fdata = malloc(allocation_size + 1)) == NULL) {
 		fatal("Out of memory.");
 	}
-	fdata[len] = '\0';
 
-	if (fread(fdata, 1, len, f) != len) {
-		fatal("Read file %s failed: %s", path, strerror(errno));
+	while (1) {
+		total_read += fread(fdata + total_read, 1, allocation_size - total_read, f);
+		if (ferror(f)) {
+			if (errno == EINTR) {
+				continue;
+			}
+			fatal("Read from %s failed: %s", path, strerror(errno));
+		}
+		if (feof(f)) {
+			break;
+		}
+		if (total_read == allocation_size) {
+			if (allocation_size > SIZE_MAX / 2) {
+				fatal("Out of memory.");
+			}
+			allocation_size *= 2;
+			if ((realloc_result = realloc(fdata, allocation_size + 1)) == NULL) {
+				free(fdata);
+				fatal("Out of memory.");
+			}
+			fdata = realloc_result;
+		}
+	}
+	if (f != stdin) {
+		fclose(f);
 	}
-	fclose(f);
+	fdata[total_read] = '\0';
 	*datap = fdata;
-	*lenp  = len;
+	*lenp = total_read;
 }
 
 static void


Test patch:
diff --git a/tools/nngcat/nngcat_stdin_pipe_test.sh b/tools/nngcat/nngcat_stdin_pipe_test.sh
new file mode 100755
index 00000000..5fec0ab7
--- /dev/null
+++ b/tools/nngcat/nngcat_stdin_pipe_test.sh
@@ -0,0 +1,44 @@
+#!/bin/bash
+
+#
+# Copyright 2018 Staysail Systems, Inc. <info@staysail.tech>
+# Copyright 2018 Capitar IT Group BV <info@capitar.com>
+# Copyright 2020 Lager Data, Inc. <support@lagerdata.com>
+#
+# This software is supplied under the terms of the MIT License, a
+# copy of which should be located in the distribution where this
+# file was obtained (LICENSE.txt).  A copy of the license may also be
+# found online at https://opensource.org/licenses/MIT.
+#
+
+NNGCAT=${NNGCAT:=$1}
+NNGCAT=${NNGCAT:-./nngcat}
+ADDR="ipc:///tmp/nngcat_stdin_pipe_test"
+OUTPUT=/tmp/nngcat_stdin_pipe_test.$$.out
+
+echo -n "Verify reading from stdin pipe: "
+
+trap "rm $OUTPUT" 0
+
+${NNGCAT} --listen ${ADDR} --count=1 --recv-timeout=3 --recv-maxsz=0 --pull0 --raw > $OUTPUT 2>/dev/null &
+bgid=$!
+
+sleep 1
+# for speed of execution, run these in the background, they should be ignored
+echo "hello world" | ${NNGCAT} --connect ${ADDR} --delay=1 --push0 --file -
+wait "$bgid" 2>/dev/null
+
+sum=$(cksum ${OUTPUT})
+sum=${sum%% *}
+
+# This matches "hello world\n" since echo adds a trailing newline
+if [[ ${sum} == 3733384285 ]]
+then
+	echo "pass"
+	exit 0
+fi
+echo "FAIL: Checksum failed (Wanted 3733384285 got ${sum})"
+echo "OUTPUT:"
+ls -la ${OUTPUT}
+
+exit 1