diff options
author | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-15 20:22:03 +0000 |
---|---|---|
committer | Daniel Baumann <daniel.baumann@progress-linux.org> | 2024-04-15 20:22:03 +0000 |
commit | ffccd5b2b05243e7976db80f90f453dccfae9886 (patch) | |
tree | 39a43152d27f7390d8f7a6fb276fa6887f87c6e8 /tests/src/vfs/extfs/helpers-list/mc_parse_ls_l.c | |
parent | Initial commit. (diff) | |
download | mc-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.c | 409 |
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; +} + +/* ------------------------------------------------------------------------------ */ |