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

Jan Stancek
2016-07-14T10:35:22Z

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