root/tests/src/vfs/extfs/helpers-list/mc_parse_ls_l.c

/* [previous][next][first][last][top][bottom][index][help]  */

DEFINITIONS

This source file includes following definitions.
  1. parse_format_name_argument
  2. parse_command_line
  3. my_itoa
  4. symbolic_uid
  5. symbolic_gid
  6. chomp
  7. string_date
  8. message
  9. yaml_dump_stbuf
  10. yaml_dump_string
  11. yaml_dump_record
  12. ls_dump_stbuf
  13. ls_dump_record
  14. process_ls_line
  15. process_input
  16. main

   1 /*
   2    A parser for file-listings formatted like 'ls -l'.
   3 
   4    Copyright (C) 2016-2024
   5    Free Software Foundation, Inc.
   6 
   7    This file is part of the Midnight Commander.
   8 
   9    The Midnight Commander is free software: you can redistribute it
  10    and/or modify it under the terms of the GNU General Public License as
  11    published by the Free Software Foundation, either version 3 of the License,
  12    or (at your option) any later version.
  13 
  14    The Midnight Commander is distributed in the hope that it will be useful,
  15    but WITHOUT ANY WARRANTY; without even the implied warranty of
  16    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  17    GNU General Public License for more details.
  18 
  19    You should have received a copy of the GNU General Public License
  20    along with this program.  If not, see <http://www.gnu.org/licenses/>.
  21  */
  22 
  23 /** \file
  24  *  \brief A parser for file-listings formatted like 'ls -l'.
  25  *
  26  * This program parses file-listings the same way MC does.
  27  * It's basically a wrapper around vfs_parse_ls_lga().
  28  * You can feed it the output of any of extfs's helpers.
  29  *
  30  * After parsing, the data is written out to stdout. The output
  31  * format can be either YAML or a format similar to 'ls -l'.
  32  *
  33  * This program is the main tool our tester script is going to use.
  34  */
  35 
  36 #include <config.h>
  37 
  38 #include <stdio.h>
  39 #include <stdlib.h>
  40 
  41 #include "lib/global.h"
  42 
  43 #include "lib/vfs/utilvfs.h"    /* vfs_parse_ls_lga() */
  44 #include "lib/util.h"           /* string_perm() */
  45 #include "lib/timefmt.h"        /* FMT_LOCALTIME */
  46 #include "lib/widget.h"         /* for the prototype of message() only */
  47 
  48 /*** global variables ****************************************************************************/
  49 
  50 /*** file scope macro definitions ****************************************************************/
  51 
  52 /*** file scope type declarations ****************************************************************/
  53 
  54 typedef enum
  55 {
  56     FORMAT_YAML,
  57     FORMAT_LS
  58 } output_format_t;
  59 
  60 /*** forward declarations (file scope functions) *************************************************/
  61 
  62 static gboolean
  63 parse_format_name_argument (const gchar * option_name, const gchar * value, gpointer data,
  64                             GError ** error);
  65 
  66 /*** file scope variables ************************************************************************/
  67 
  68 /* Command-line options. */
  69 static gboolean opt_drop_mtime = FALSE;
  70 static gboolean opt_drop_ids = FALSE;
  71 static gboolean opt_symbolic_ids = FALSE;
  72 static output_format_t opt_output_format = FORMAT_LS;
  73 
  74 /* Misc. */
  75 static int error_count = 0;
  76 
  77 static GOptionEntry entries[] = {
  78     {"drop-mtime", 0, 0, G_OPTION_ARG_NONE, &opt_drop_mtime, "Don't include mtime in the output.",
  79      NULL},
  80     {"drop-ids", 0, 0, G_OPTION_ARG_NONE, &opt_drop_ids, "Don't include uid/gid in the output.",
  81      NULL},
  82     {"symbolic-ids", 0, 0, G_OPTION_ARG_NONE, &opt_symbolic_ids,
  83      "Print the strings '<<uid>>'/'<<gid>>' instead of the numeric IDs when they match the process' uid/gid.",
  84      NULL},
  85     {"format", 'f', 0, G_OPTION_ARG_CALLBACK, parse_format_name_argument,
  86      "Output format. Default: ls.", "<ls|yaml>"},
  87     G_OPTION_ENTRY_NULL
  88 };
  89 
  90 /* --------------------------------------------------------------------------------------------- */
  91 /*** file scope functions ************************************************************************/
  92 /* --------------------------------------------------------------------------------------------- */
  93 /**
  94  * Command-line handling.
  95  */
  96 /* --------------------------------------------------------------------------------------------- */
  97 
  98 static gboolean
  99 parse_format_name_argument (const gchar *option_name, const gchar *value, gpointer data,
     /* [previous][next][first][last][top][bottom][index][help]  */
 100                             GError **error)
 101 {
 102     (void) option_name;
 103     (void) data;
 104 
 105     if (strcmp (value, "yaml") == 0)
 106         opt_output_format = FORMAT_YAML;
 107     else if (strcmp (value, "ls") == 0)
 108         opt_output_format = FORMAT_LS;
 109     else
 110     {
 111         g_set_error (error, MC_ERROR, G_OPTION_ERROR_FAILED, "unknown output format '%s'", value);
 112         return FALSE;
 113     }
 114 
 115     return TRUE;
 116 }
 117 
 118 /* --------------------------------------------------------------------------------------------- */
 119 
 120 static gboolean
 121 parse_command_line (int *argc, char **argv[])
     /* [previous][next][first][last][top][bottom][index][help]  */
 122 {
 123     GError *error = NULL;
 124     GOptionContext *context;
 125 
 126     context =
 127         g_option_context_new
 128         ("- Parses its input, which is expected to be in a format similar to 'ls -l', and writes the result to stdout.");
 129     g_option_context_add_main_entries (context, entries, NULL);
 130     if (!g_option_context_parse (context, argc, argv, &error))
 131     {
 132         g_print ("option parsing failed: %s\n", error->message);
 133         g_error_free (error);
 134 
 135         return FALSE;
 136     }
 137 
 138     return TRUE;
 139 }
 140 
 141 /* --------------------------------------------------------------------------------------------- */
 142 /**
 143  * Utility functions.
 144  */
 145 /* --------------------------------------------------------------------------------------------- */
 146 
 147 static const char *
 148 my_itoa (int i)
     /* [previous][next][first][last][top][bottom][index][help]  */
 149 {
 150     static char buf[BUF_SMALL];
 151 
 152     sprintf (buf, "%d", i);
 153     return buf;
 154 }
 155 
 156 /* --------------------------------------------------------------------------------------------- */
 157 
 158 /**
 159  * Returns the uid as-is, or as "<<uid>>" if the same as current user.
 160  */
 161 static const char *
 162 symbolic_uid (uid_t uid)
     /* [previous][next][first][last][top][bottom][index][help]  */
 163 {
 164     return (opt_symbolic_ids && uid == getuid ())? "<<uid>>" : my_itoa ((int) uid);
 165 }
 166 
 167 /* --------------------------------------------------------------------------------------------- */
 168 
 169 static const char *
 170 symbolic_gid (gid_t gid)
     /* [previous][next][first][last][top][bottom][index][help]  */
 171 {
 172     return (opt_symbolic_ids && gid == getgid ())? "<<gid>>" : my_itoa ((int) gid);
 173 }
 174 
 175 /* --------------------------------------------------------------------------------------------- */
 176 
 177 /**
 178  * Cuts off a string's line-end (as in Perl).
 179  */
 180 static void
 181 chomp (char *s)
     /* [previous][next][first][last][top][bottom][index][help]  */
 182 {
 183     int i;
 184 
 185     i = strlen (s);
 186 
 187     /* Code taken from vfs_parse_ls_lga(), with modifications. */
 188     if ((--i >= 0) && (s[i] == '\r' || s[i] == '\n'))
 189         s[i] = '\0';
 190     if ((--i >= 0) && (s[i] == '\r' || s[i] == '\n'))
 191         s[i] = '\0';
 192 }
 193 
 194 /* --------------------------------------------------------------------------------------------- */
 195 
 196 static const char *
 197 string_date (time_t t)
     /* [previous][next][first][last][top][bottom][index][help]  */
 198 {
 199     static char buf[BUF_SMALL];
 200 
 201     /*
 202      * If we ever want to be able to re-parse the output of this program,
 203      * we'll need to use the American brain-damaged MM-DD-YYYY instead of
 204      * YYYY-MM-DD because vfs_parse_ls_lga() doesn't currently recognize
 205      * the latter.
 206      */
 207     FMT_LOCALTIME (buf, sizeof buf, "%Y-%m-%d %H:%M:%S", t);
 208     return buf;
 209 }
 210 
 211 /* --------------------------------------------------------------------------------------------- */
 212 
 213 /**
 214  * Override MC's message().
 215  *
 216  * vfs_parse_ls_lga() calls this on error. Since MC's uses tty/widgets, it
 217  * will crash us. We replace it with a plain version.
 218  */
 219 void
 220 message (int flags, const char *title, const char *text, ...)
     /* [previous][next][first][last][top][bottom][index][help]  */
 221 {
 222     char *p;
 223     va_list ap;
 224 
 225     (void) flags;
 226     (void) title;
 227 
 228     va_start (ap, text);
 229     p = g_strdup_vprintf (text, ap);
 230     va_end (ap);
 231     printf ("message(): vfs_parse_ls_lga(): parsing error at: %s\n", p);
 232     g_free (p);
 233 }
 234 
 235 /* --------------------------------------------------------------------------------------------- */
 236 /**
 237  * YAML output format.
 238  */
 239 /* --------------------------------------------------------------------------------------------- */
 240 
 241 static void
 242 yaml_dump_stbuf (const struct stat *st)
     /* [previous][next][first][last][top][bottom][index][help]  */
 243 {
 244     /* Various casts and flags here were taken/inspired by info_show_info(). */
 245     printf ("  perm: %s\n", string_perm (st->st_mode));
 246     if (!opt_drop_ids)
 247     {
 248         printf ("  uid: %s\n", symbolic_uid (st->st_uid));
 249         printf ("  gid: %s\n", symbolic_gid (st->st_gid));
 250     }
 251     printf ("  size: %" PRIuMAX "\n", (uintmax_t) st->st_size);
 252     printf ("  nlink: %d\n", (int) st->st_nlink);
 253     if (!opt_drop_mtime)
 254         printf ("  mtime: %s\n", string_date (st->st_mtime));
 255 }
 256 
 257 /* --------------------------------------------------------------------------------------------- */
 258 
 259 static void
 260 yaml_dump_string (const char *name, const char *val)
     /* [previous][next][first][last][top][bottom][index][help]  */
 261 {
 262     char *q;
 263 
 264     q = g_shell_quote (val);
 265     printf ("  %s: %s\n", name, q);
 266     g_free (q);
 267 }
 268 
 269 /* --------------------------------------------------------------------------------------------- */
 270 
 271 static void
 272 yaml_dump_record (gboolean success, const char *input_line, const struct stat *st,
     /* [previous][next][first][last][top][bottom][index][help]  */
 273                   const char *filename, const char *linkname)
 274 {
 275     printf ("-\n");             /* Start new item in the list. */
 276 
 277     if (success)
 278     {
 279         if (filename != NULL)
 280             yaml_dump_string ("name", filename);
 281         if (linkname != NULL)
 282             yaml_dump_string ("linkname", linkname);
 283         yaml_dump_stbuf (st);
 284     }
 285     else
 286     {
 287         yaml_dump_string ("cannot parse input line", input_line);
 288     }
 289 }
 290 
 291 /* --------------------------------------------------------------------------------------------- */
 292 /**
 293  * 'ls' output format.
 294  */
 295 /* --------------------------------------------------------------------------------------------- */
 296 
 297 static void
 298 ls_dump_stbuf (const struct stat *st)
     /* [previous][next][first][last][top][bottom][index][help]  */
 299 {
 300     /* Various casts and flags here were taken/inspired by info_show_info(). */
 301     printf ("%s %3d ", string_perm (st->st_mode), (int) st->st_nlink);
 302     if (!opt_drop_ids)
 303     {
 304         printf ("%8s ", symbolic_uid (st->st_uid));
 305         printf ("%8s ", symbolic_gid (st->st_gid));
 306     }
 307     printf ("%10" PRIuMAX " ", (uintmax_t) st->st_size);
 308     if (!opt_drop_mtime)
 309         printf ("%s ", string_date (st->st_mtime));
 310 }
 311 
 312 /* --------------------------------------------------------------------------------------------- */
 313 
 314 static void
 315 ls_dump_record (gboolean success, const char *input_line, const struct stat *st,
     /* [previous][next][first][last][top][bottom][index][help]  */
 316                 const char *filename, const char *linkname)
 317 {
 318     if (success)
 319     {
 320         ls_dump_stbuf (st);
 321         if (filename != NULL)
 322             printf ("%s", filename);
 323         if (linkname != NULL)
 324             printf (" -> %s", linkname);
 325         printf ("\n");
 326     }
 327     else
 328     {
 329         printf ("cannot parse input line: '%s'\n", input_line);
 330     }
 331 }
 332 
 333 /* ------------------------------------------------------------------------------ */
 334 /**
 335  * Main code.
 336  */
 337 /* ------------------------------------------------------------------------------ */
 338 
 339 static void
 340 process_ls_line (const char *line)
     /* [previous][next][first][last][top][bottom][index][help]  */
 341 {
 342     struct stat st;
 343     char *filename, *linkname;
 344     gboolean success;
 345 
 346     memset (&st, 0, sizeof st);
 347     filename = NULL;
 348     linkname = NULL;
 349 
 350     success = vfs_parse_ls_lga (line, &st, &filename, &linkname, NULL);
 351 
 352     if (!success)
 353         error_count++;
 354 
 355     if (opt_output_format == FORMAT_YAML)
 356         yaml_dump_record (success, line, &st, filename, linkname);
 357     else
 358         ls_dump_record (success, line, &st, filename, linkname);
 359 
 360     g_free (filename);
 361     g_free (linkname);
 362 }
 363 
 364 /* ------------------------------------------------------------------------------ */
 365 
 366 static void
 367 process_input (FILE *input)
     /* [previous][next][first][last][top][bottom][index][help]  */
 368 {
 369     char line[BUF_4K];
 370 
 371     while (fgets (line, sizeof line, input) != NULL)
 372     {
 373         chomp (line);           /* Not mandatory. Makes error messages nicer. */
 374         if (strncmp (line, "total ", 6) == 0)   /* Convenience only: makes 'ls -l' parse cleanly. */
 375             continue;
 376         process_ls_line (line);
 377     }
 378 }
 379 
 380 /* ------------------------------------------------------------------------------ */
 381 
 382 int
 383 main (int argc, char *argv[])
     /* [previous][next][first][last][top][bottom][index][help]  */
 384 {
 385     FILE *input;
 386 
 387     if (!parse_command_line (&argc, &argv))
 388         return EXIT_FAILURE;
 389 
 390     if (argc >= 2)
 391     {
 392         input = fopen (argv[1], "r");
 393         if (input == NULL)
 394         {
 395             perror (argv[1]);
 396             return EXIT_FAILURE;
 397         }
 398     }
 399     else
 400     {
 401         input = stdin;
 402     }
 403 
 404     process_input (input);
 405 
 406     return (error_count > 0) ? EXIT_FAILURE : EXIT_SUCCESS;
 407 }
 408 
 409 /* ------------------------------------------------------------------------------ */

/* [previous][next][first][last][top][bottom][index][help]  */