summaryrefslogtreecommitdiffstats
path: root/tests/src/vfs/extfs/helpers-list/mc_parse_ls_l.c
diff options
context:
space:
mode:
authorDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-15 20:22:03 +0000
committerDaniel Baumann <daniel.baumann@progress-linux.org>2024-04-15 20:22:03 +0000
commitffccd5b2b05243e7976db80f90f453dccfae9886 (patch)
tree39a43152d27f7390d8f7a6fb276fa6887f87c6e8 /tests/src/vfs/extfs/helpers-list/mc_parse_ls_l.c
parentInitial commit. (diff)
downloadmc-ffccd5b2b05243e7976db80f90f453dccfae9886.tar.xz
mc-ffccd5b2b05243e7976db80f90f453dccfae9886.zip
Adding upstream version 3:4.8.30.upstream/3%4.8.30
Signed-off-by: Daniel Baumann <daniel.baumann@progress-linux.org>
Diffstat (limited to '')
-rw-r--r--tests/src/vfs/extfs/helpers-list/mc_parse_ls_l.c409
1 files changed, 409 insertions, 0 deletions
diff --git a/tests/src/vfs/extfs/helpers-list/mc_parse_ls_l.c b/tests/src/vfs/extfs/helpers-list/mc_parse_ls_l.c
new file mode 100644
index 0000000..7c6ef18
--- /dev/null
+++ b/tests/src/vfs/extfs/helpers-list/mc_parse_ls_l.c
@@ -0,0 +1,409 @@
+/*
+ A parser for file-listings formatted like 'ls -l'.
+
+ Copyright (C) 2016-2023
+ Free Software Foundation, Inc.
+
+ This file is part of the Midnight Commander.
+
+ The Midnight Commander 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 3 of the License,
+ or (at your option) any later version.
+
+ The Midnight Commander 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/>.
+ */
+
+/** \file
+ * \brief A parser for file-listings formatted like 'ls -l'.
+ *
+ * This program parses file-listings the same way MC does.
+ * It's basically a wrapper around vfs_parse_ls_lga().
+ * You can feed it the output of any of extfs's helpers.
+ *
+ * After parsing, the data is written out to stdout. The output
+ * format can be either YAML or a format similar to 'ls -l'.
+ *
+ * This program is the main tool our tester script is going to use.
+ */
+
+#include <config.h>
+
+#include <stdio.h>
+#include <stdlib.h>
+
+#include "lib/global.h"
+
+#include "lib/vfs/utilvfs.h" /* vfs_parse_ls_lga() */
+#include "lib/util.h" /* string_perm() */
+#include "lib/timefmt.h" /* FMT_LOCALTIME */
+#include "lib/widget.h" /* for the prototype of message() only */
+
+/*** global variables ****************************************************************************/
+
+/*** file scope macro definitions ****************************************************************/
+
+/*** file scope type declarations ****************************************************************/
+
+typedef enum
+{
+ FORMAT_YAML,
+ FORMAT_LS
+} output_format_t;
+
+/*** forward declarations (file scope functions) *************************************************/
+
+static gboolean
+parse_format_name_argument (const gchar * option_name, const gchar * value, gpointer data,
+ GError ** error);
+
+/*** file scope variables ************************************************************************/
+
+/* Command-line options. */
+static gboolean opt_drop_mtime = FALSE;
+static gboolean opt_drop_ids = FALSE;
+static gboolean opt_symbolic_ids = FALSE;
+static output_format_t opt_output_format = FORMAT_LS;
+
+/* Misc. */
+static int error_count = 0;
+
+static GOptionEntry entries[] = {
+ {"drop-mtime", 0, 0, G_OPTION_ARG_NONE, &opt_drop_mtime, "Don't include mtime in the output.",
+ NULL},
+ {"drop-ids", 0, 0, G_OPTION_ARG_NONE, &opt_drop_ids, "Don't include uid/gid in the output.",
+ NULL},
+ {"symbolic-ids", 0, 0, G_OPTION_ARG_NONE, &opt_symbolic_ids,
+ "Print the strings '<<uid>>'/'<<gid>>' instead of the numeric IDs when they match the process' uid/gid.",
+ NULL},
+ {"format", 'f', 0, G_OPTION_ARG_CALLBACK, parse_format_name_argument,
+ "Output format. Default: ls.", "<ls|yaml>"},
+ G_OPTION_ENTRY_NULL
+};
+
+/* --------------------------------------------------------------------------------------------- */
+/*** file scope functions ************************************************************************/
+/* --------------------------------------------------------------------------------------------- */
+/**
+ * Command-line handling.
+ */
+/* --------------------------------------------------------------------------------------------- */
+
+static gboolean
+parse_format_name_argument (const gchar * option_name, const gchar * value, gpointer data,
+ GError ** error)
+{
+ (void) option_name;
+ (void) data;
+
+ if (strcmp (value, "yaml") == 0)
+ opt_output_format = FORMAT_YAML;
+ else if (strcmp (value, "ls") == 0)
+ opt_output_format = FORMAT_LS;
+ else
+ {
+ g_set_error (error, MC_ERROR, G_OPTION_ERROR_FAILED, "unknown output format '%s'", value);
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/* --------------------------------------------------------------------------------------------- */
+
+static gboolean
+parse_command_line (int *argc, char **argv[])
+{
+ GError *error = NULL;
+ GOptionContext *context;
+
+ context =
+ g_option_context_new
+ ("- Parses its input, which is expected to be in a format similar to 'ls -l', and writes the result to stdout.");
+ g_option_context_add_main_entries (context, entries, NULL);
+ if (!g_option_context_parse (context, argc, argv, &error))
+ {
+ g_print ("option parsing failed: %s\n", error->message);
+ g_error_free (error);
+
+ return FALSE;
+ }
+
+ return TRUE;
+}
+
+/* --------------------------------------------------------------------------------------------- */
+/**
+ * Utility functions.
+ */
+/* --------------------------------------------------------------------------------------------- */
+
+static const char *
+my_itoa (int i)
+{
+ static char buf[BUF_SMALL];
+
+ sprintf (buf, "%d", i);
+ return buf;
+}
+
+/* --------------------------------------------------------------------------------------------- */
+
+/**
+ * Returns the uid as-is, or as "<<uid>>" if the same as current user.
+ */
+static const char *
+symbolic_uid (uid_t uid)
+{
+ return (opt_symbolic_ids && uid == getuid ())? "<<uid>>" : my_itoa ((int) uid);
+}
+
+/* --------------------------------------------------------------------------------------------- */
+
+static const char *
+symbolic_gid (gid_t gid)
+{
+ return (opt_symbolic_ids && gid == getgid ())? "<<gid>>" : my_itoa ((int) gid);
+}
+
+/* --------------------------------------------------------------------------------------------- */
+
+/**
+ * Cuts off a string's line-end (as in Perl).
+ */
+static void
+chomp (char *s)
+{
+ int i;
+
+ i = strlen (s);
+
+ /* Code taken from vfs_parse_ls_lga(), with modifications. */
+ if ((--i >= 0) && (s[i] == '\r' || s[i] == '\n'))
+ s[i] = '\0';
+ if ((--i >= 0) && (s[i] == '\r' || s[i] == '\n'))
+ s[i] = '\0';
+}
+
+/* --------------------------------------------------------------------------------------------- */
+
+static const char *
+string_date (time_t t)
+{
+ static char buf[BUF_SMALL];
+
+ /*
+ * If we ever want to be able to re-parse the output of this program,
+ * we'll need to use the American brain-damaged MM-DD-YYYY instead of
+ * YYYY-MM-DD because vfs_parse_ls_lga() doesn't currently recognize
+ * the latter.
+ */
+ FMT_LOCALTIME (buf, sizeof buf, "%Y-%m-%d %H:%M:%S", t);
+ return buf;
+}
+
+/* --------------------------------------------------------------------------------------------- */
+
+/**
+ * Override MC's message().
+ *
+ * vfs_parse_ls_lga() calls this on error. Since MC's uses tty/widgets, it
+ * will crash us. We replace it with a plain version.
+ */
+void
+message (int flags, const char *title, const char *text, ...)
+{
+ char *p;
+ va_list ap;
+
+ (void) flags;
+ (void) title;
+
+ va_start (ap, text);
+ p = g_strdup_vprintf (text, ap);
+ va_end (ap);
+ printf ("message(): vfs_parse_ls_lga(): parsing error at: %s\n", p);
+ g_free (p);
+}
+
+/* --------------------------------------------------------------------------------------------- */
+/**
+ * YAML output format.
+ */
+/* --------------------------------------------------------------------------------------------- */
+
+static void
+yaml_dump_stbuf (const struct stat *st)
+{
+ /* Various casts and flags here were taken/inspired by info_show_info(). */
+ printf (" perm: %s\n", string_perm (st->st_mode));
+ if (!opt_drop_ids)
+ {
+ printf (" uid: %s\n", symbolic_uid (st->st_uid));
+ printf (" gid: %s\n", symbolic_gid (st->st_gid));
+ }
+ printf (" size: %" PRIuMAX "\n", (uintmax_t) st->st_size);
+ printf (" nlink: %d\n", (int) st->st_nlink);
+ if (!opt_drop_mtime)
+ printf (" mtime: %s\n", string_date (st->st_mtime));
+}
+
+/* --------------------------------------------------------------------------------------------- */
+
+static void
+yaml_dump_string (const char *name, const char *val)
+{
+ char *q;
+
+ q = g_shell_quote (val);
+ printf (" %s: %s\n", name, q);
+ g_free (q);
+}
+
+/* --------------------------------------------------------------------------------------------- */
+
+static void
+yaml_dump_record (gboolean success, const char *input_line, const struct stat *st,
+ const char *filename, const char *linkname)
+{
+ printf ("-\n"); /* Start new item in the list. */
+
+ if (success)
+ {
+ if (filename != NULL)
+ yaml_dump_string ("name", filename);
+ if (linkname != NULL)
+ yaml_dump_string ("linkname", linkname);
+ yaml_dump_stbuf (st);
+ }
+ else
+ {
+ yaml_dump_string ("cannot parse input line", input_line);
+ }
+}
+
+/* --------------------------------------------------------------------------------------------- */
+/**
+ * 'ls' output format.
+ */
+/* --------------------------------------------------------------------------------------------- */
+
+static void
+ls_dump_stbuf (const struct stat *st)
+{
+ /* Various casts and flags here were taken/inspired by info_show_info(). */
+ printf ("%s %3d ", string_perm (st->st_mode), (int) st->st_nlink);
+ if (!opt_drop_ids)
+ {
+ printf ("%8s ", symbolic_uid (st->st_uid));
+ printf ("%8s ", symbolic_gid (st->st_gid));
+ }
+ printf ("%10" PRIuMAX " ", (uintmax_t) st->st_size);
+ if (!opt_drop_mtime)
+ printf ("%s ", string_date (st->st_mtime));
+}
+
+/* --------------------------------------------------------------------------------------------- */
+
+static void
+ls_dump_record (gboolean success, const char *input_line, const struct stat *st,
+ const char *filename, const char *linkname)
+{
+ if (success)
+ {
+ ls_dump_stbuf (st);
+ if (filename != NULL)
+ printf ("%s", filename);
+ if (linkname != NULL)
+ printf (" -> %s", linkname);
+ printf ("\n");
+ }
+ else
+ {
+ printf ("cannot parse input line: '%s'\n", input_line);
+ }
+}
+
+/* ------------------------------------------------------------------------------ */
+/**
+ * Main code.
+ */
+/* ------------------------------------------------------------------------------ */
+
+static void
+process_ls_line (const char *line)
+{
+ struct stat st;
+ char *filename, *linkname;
+ gboolean success;
+
+ memset (&st, 0, sizeof st);
+ filename = NULL;
+ linkname = NULL;
+
+ success = vfs_parse_ls_lga (line, &st, &filename, &linkname, NULL);
+
+ if (!success)
+ error_count++;
+
+ if (opt_output_format == FORMAT_YAML)
+ yaml_dump_record (success, line, &st, filename, linkname);
+ else
+ ls_dump_record (success, line, &st, filename, linkname);
+
+ g_free (filename);
+ g_free (linkname);
+}
+
+/* ------------------------------------------------------------------------------ */
+
+static void
+process_input (FILE * input)
+{
+ char line[BUF_4K];
+
+ while (fgets (line, sizeof line, input) != NULL)
+ {
+ chomp (line); /* Not mandatory. Makes error messages nicer. */
+ if (strncmp (line, "total ", 6) == 0) /* Convenience only: makes 'ls -l' parse cleanly. */
+ continue;
+ process_ls_line (line);
+ }
+}
+
+/* ------------------------------------------------------------------------------ */
+
+int
+main (int argc, char *argv[])
+{
+ FILE *input;
+
+ if (!parse_command_line (&argc, &argv))
+ return EXIT_FAILURE;
+
+ if (argc >= 2)
+ {
+ input = fopen (argv[1], "r");
+ if (input == NULL)
+ {
+ perror (argv[1]);
+ return EXIT_FAILURE;
+ }
+ }
+ else
+ {
+ input = stdin;
+ }
+
+ process_input (input);
+
+ return (error_count > 0) ? EXIT_FAILURE : EXIT_SUCCESS;
+}
+
+/* ------------------------------------------------------------------------------ */