[LTP] [PATCH] lib: add [SAFE_]FILE_LINES_SCANF

Jan Stancek jstancek@redhat.com
Thu Jul 14 12:35:22 CEST 2016


This patch adds 2 macros that try to parse each line from
a file according to user-supplied (scanf) format. First line
that matches all format directives ends the search. If EOF
is reached, SAFE_ version triggers TBROK, non-SAFE_ returns
non-zero retcode to user.

Main motivation is parsing various /proc files, for example:
  if (FILE_LINES_SCANF("/proc/meminfo", "MemFree: %ld", &free))
      do_something();

  SAFE_FILE_LINES_SCANF("/proc/meminfo", "MemFree: %ld", &free);
      // automatically calls TBROK if all directives can't be matched

Signed-off-by: Jan Stancek <jstancek@redhat.com>
---
 .gitignore                      |  1 +
 include/old/old_safe_file_ops.h |  8 +++++++
 include/safe_file_ops_fn.h      |  5 +++++
 include/tst_safe_file_ops.h     |  8 +++++++
 lib/safe_file_ops.c             | 47 +++++++++++++++++++++++++++++++++++++++++
 lib/tests/tst_safe_fileops.c    | 40 +++++++++++++++++++++++++++++++++++
 6 files changed, 109 insertions(+)
 create mode 100644 lib/tests/tst_safe_fileops.c

diff --git a/.gitignore b/.gitignore
index 1b4ac3e46249..2775c2a656e5 100644
--- a/.gitignore
+++ b/.gitignore
@@ -73,3 +73,4 @@ logfile.*
 /lib/tests/tst_dataroot01
 /lib/tests/tst_dataroot02
 /lib/tests/tst_dataroot03
+/lib/tests/tst_safe_fileops
diff --git a/include/old/old_safe_file_ops.h b/include/old/old_safe_file_ops.h
index d656823e46fa..d6e2d29a9690 100644
--- a/include/old/old_safe_file_ops.h
+++ b/include/old/old_safe_file_ops.h
@@ -38,6 +38,14 @@
 	safe_file_scanf(__FILE__, __LINE__, (cleanup_fn), \
 	                (path), (fmt), ## __VA_ARGS__)
 
+#define FILE_LINES_SCANF(cleanup_fn, path, fmt, ...) \
+	file_lines_scanf(__FILE__, __LINE__, (cleanup_fn), 0, \
+			(path), (fmt), ## __VA_ARGS__)
+
+#define SAFE_FILE_LINES_SCANF(cleanup_fn, path, fmt, ...) \
+	file_lines_scanf(__FILE__, __LINE__, (cleanup_fn), 1, \
+			(path), (fmt), ## __VA_ARGS__)
+
 #define FILE_PRINTF(path, fmt, ...) \
 	file_printf(__FILE__, __LINE__, \
 	            (path), (fmt), ## __VA_ARGS__)
diff --git a/include/safe_file_ops_fn.h b/include/safe_file_ops_fn.h
index cdb0f7436909..35ec4fb1f8ee 100644
--- a/include/safe_file_ops_fn.h
+++ b/include/safe_file_ops_fn.h
@@ -35,6 +35,11 @@ void safe_file_scanf(const char *file, const int lineno,
 		     const char *path, const char *fmt, ...)
 		     __attribute__ ((format (scanf, 5, 6)));
 
+int file_lines_scanf(const char *file, const int lineno,
+		     void (*cleanup_fn)(void), int strict,
+		     const char *path, const char *fmt, ...)
+		     __attribute__ ((format (scanf, 6, 7)));
+
 /*
  * All-in-one function that lets you printf directly into a file.
  */
diff --git a/include/tst_safe_file_ops.h b/include/tst_safe_file_ops.h
index 74e679153d8a..2e4067ca15fd 100644
--- a/include/tst_safe_file_ops.h
+++ b/include/tst_safe_file_ops.h
@@ -30,6 +30,14 @@
 	safe_file_scanf(__FILE__, __LINE__, NULL, \
 	                (path), (fmt), ## __VA_ARGS__)
 
+#define FILE_LINES_SCANF(path, fmt, ...) \
+	file_lines_scanf(__FILE__, __LINE__, NULL, 0,\
+			(path), (fmt), ## __VA_ARGS__)
+
+#define SAFE_FILE_LINES_SCANF(path, fmt, ...) \
+	file_lines_scanf(__FILE__, __LINE__, NULL, 1,\
+			(path), (fmt), ## __VA_ARGS__)
+
 #define SAFE_FILE_PRINTF(path, fmt, ...) \
 	safe_file_printf(__FILE__, __LINE__, NULL, \
 	                 (path), (fmt), ## __VA_ARGS__)
diff --git a/lib/safe_file_ops.c b/lib/safe_file_ops.c
index dff85cd83fec..01f64ed3251b 100644
--- a/lib/safe_file_ops.c
+++ b/lib/safe_file_ops.c
@@ -169,6 +169,53 @@ void safe_file_scanf(const char *file, const int lineno,
 	}
 }
 
+
+/*
+ * Try to parse each line from file specified by 'path' according
+ * to scanf format 'fmt'. If all fields could be parsed, stop and
+ * return 0, otherwise continue or return 1 if EOF is reached.
+ */
+int file_lines_scanf(const char *file, const int lineno,
+		     void (*cleanup_fn)(void), int strict,
+		     const char *path, const char *fmt, ...)
+{
+	FILE *fp;
+	int ret = 0;
+	int arg_count = 0;
+	char line[BUFSIZ];
+	va_list ap;
+
+	if (!fmt) {
+		tst_brkm(TBROK, cleanup_fn, "pattern is NULL, %s:%d",
+			file, lineno);
+	}
+
+	fp = fopen(path, "r");
+	if (fp == NULL) {
+		tst_brkm(TBROK | TERRNO, cleanup_fn,
+			"Failed to open FILE '%s' for reading at %s:%d",
+			path, file, lineno);
+	}
+
+	arg_count = count_scanf_conversions(fmt);
+
+	while (fgets(line, BUFSIZ, fp) != NULL) {
+		va_start(ap, fmt);
+		ret = vsscanf(line, fmt, ap);
+		va_end(ap);
+
+		if (ret == arg_count)
+			break;
+	}
+	fclose(fp);
+
+	if (strict && ret != arg_count)
+		tst_brkm(TBROK, cleanup_fn, "Expected %i conversions got %i"
+			" at %s:%d", arg_count, ret, file, lineno);
+
+	return !(ret == arg_count);
+}
+
 int file_printf(const char *file, const int lineno,
 		      const char *path, const char *fmt, ...)
 {
diff --git a/lib/tests/tst_safe_fileops.c b/lib/tests/tst_safe_fileops.c
new file mode 100644
index 000000000000..bdf4e9d07027
--- /dev/null
+++ b/lib/tests/tst_safe_fileops.c
@@ -0,0 +1,40 @@
+/*
+ * Copyright (c) 2016 Linux Test Project
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <stdio.h>
+#include "tst_test.h"
+
+static void do_test(void)
+{
+	long free;
+	long nproc;
+	long dummy;
+
+	SAFE_FILE_LINES_SCANF("/proc/meminfo", "MemFree: %ld", &free);
+	if (FILE_LINES_SCANF("/proc/stat", "processes %ld", &nproc))
+		tst_brk(TBROK, "Could not parse processes");
+	tst_res(TPASS, "Free: %ld, nproc: %ld", free, nproc);
+
+	if (FILE_LINES_SCANF("/proc/stat", "non-existent %ld", &dummy))
+		tst_res(TPASS, "non-existent not found");
+	SAFE_FILE_LINES_SCANF("/proc/stat", "non-existent %ld", &dummy);
+}
+
+static struct tst_test test = {
+	.tid = "tst_safe_fileops",
+	.test_all = do_test,
+};
-- 
1.8.3.1



More information about the ltp mailing list