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-2019
   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 /*** file scope variables ************************************************************************/
  61 
  62 /* Command-line options. */
  63 static gboolean opt_drop_mtime = FALSE;
  64 static gboolean opt_drop_ids = FALSE;
  65 static gboolean opt_symbolic_ids = FALSE;
  66 static output_format_t opt_output_format = FORMAT_LS;
  67 
  68 /* Misc. */
  69 static int error_count = 0;
  70 
  71 /* forward declarations */
  72 static gboolean
  73 parse_format_name_argument (const gchar * option_name, const gchar * value, gpointer data,
  74                             GError ** error);
  75 
  76 static GOptionEntry entries[] = {
  77     {"drop-mtime", 0, 0, G_OPTION_ARG_NONE, &opt_drop_mtime, "Don't include mtime in the output.",
  78      NULL},
  79     {"drop-ids", 0, 0, G_OPTION_ARG_NONE, &opt_drop_ids, "Don't include uid/gid in the output.",
  80      NULL},
  81     {"symbolic-ids", 0, 0, G_OPTION_ARG_NONE, &opt_symbolic_ids,
  82      "Print the strings '<<uid>>'/'<<gid>>' instead of the numeric IDs when they match the process' uid/gid.",
  83      NULL},
  84     {"format", 'f', 0, G_OPTION_ARG_CALLBACK, parse_format_name_argument,
  85      "Output format. Default: ls.", "<ls|yaml>"},
  86     {NULL, '\0', 0, 0, NULL, NULL, NULL}        /* Make the compiler shut up by initializing everything. */
  87 };
  88 
  89 /*** file scope functions ************************************************************************/
  90 /* --------------------------------------------------------------------------------------------- */
  91 /**
  92  * Command-line handling.
  93  */
  94 /* --------------------------------------------------------------------------------------------- */
  95 
  96 static gboolean
  97 parse_format_name_argument (const gchar * option_name, const gchar * value, gpointer data,
     /* [previous][next][first][last][top][bottom][index][help]  */
  98                             GError ** error)
  99 {
 100     (void) option_name;
 101     (void) data;
 102 
 103     if (strcmp (value, "yaml") == 0)
 104         opt_output_format = FORMAT_YAML;
 105     else if (strcmp (value, "ls") == 0)
 106         opt_output_format = FORMAT_LS;
 107     else
 108     {
 109         g_set_error (error, MC_ERROR, G_OPTION_ERROR_FAILED, "unknown output format '%s'", value);
 110         return FALSE;
 111     }
 112 
 113     return TRUE;
 114 }
 115 
 116 /* --------------------------------------------------------------------------------------------- */
 117 
 118 static gboolean
 119 parse_command_line (int *argc, char **argv[])
     /* [previous][next][first][last][top][bottom][index][help]  */
 120 {
 121     GError *error = NULL;
 122     GOptionContext *context;
 123 
 124     context =
 125         g_option_context_new
 126         ("- Parses its input, which is expected to be in a format similar to 'ls -l', and writes the result to stdout.");
 127     g_option_context_add_main_entries (context, entries, NULL);
 128     if (!g_option_context_parse (context, argc, argv, &error))
 129     {
 130         g_print ("option parsing failed: %s\n", error->message);
 131         g_error_free (error);
 132 
 133         return FALSE;
 134     }
 135 
 136     return TRUE;
 137 }
 138 
 139 /* --------------------------------------------------------------------------------------------- */
 140 /**
 141  * Utility functions.
 142  */
 143 /* --------------------------------------------------------------------------------------------- */
 144 
 145 static const char *
 146 my_itoa (int i)
     /* [previous][next][first][last][top][bottom][index][help]  */
 147 {
 148     static char buf[BUF_SMALL];
 149 
 150     sprintf (buf, "%d", i);
 151     return buf;
 152 }
 153 
 154 /* --------------------------------------------------------------------------------------------- */
 155 
 156 /**
 157  * Returns the uid as-is, or as "<<uid>>" if the same as current user.
 158  */
 159 static const char *
 160 symbolic_uid (uid_t uid)
     /* [previous][next][first][last][top][bottom][index][help]  */
 161 {
 162     return (opt_symbolic_ids && uid == getuid ())? "<<uid>>" : my_itoa ((int) uid);
 163 }
 164 
 165 /* --------------------------------------------------------------------------------------------- */
 166 
 167 static const char *
 168 symbolic_gid (gid_t gid)
     /* [previous][next][first][last][top][bottom][index][help]  */
 169 {
 170     return (opt_symbolic_ids && gid == getgid ())? "<<gid>>" : my_itoa ((int) gid);
 171 }
 172 
 173 /* --------------------------------------------------------------------------------------------- */
 174 
 175 /**
 176  * Cuts off a string's line-end (as in Perl).
 177  */
 178 static void
 179 chomp (char *s)
     /* [previous][next][first][last][top][bottom][index][help]  */
 180 {
 181     int i;
 182 
 183     i = strlen (s);
 184 
 185     /* Code taken from vfs_parse_ls_lga(), with modifications. */
 186     if ((--i >= 0) && (s[i] == '\r' || s[i] == '\n'))
 187         s[i] = '\0';
 188     if ((--i >= 0) && (s[i] == '\r' || s[i] == '\n'))
 189         s[i] = '\0';
 190 }
 191 
 192 /* --------------------------------------------------------------------------------------------- */
 193 
 194 static const char *
 195 string_date (time_t t)
     /* [previous][next][first][last][top][bottom][index][help]  */
 196 {
 197     static char buf[BUF_SMALL];
 198 
 199     /*
 200      * If we ever want to be able to re-parse the output of this program,
 201      * we'll need to use the American brain-damaged MM-DD-YYYY instead of
 202      * YYYY-MM-DD because vfs_parse_ls_lga() doesn't currently recognize
 203      * the latter.
 204      */
 205     FMT_LOCALTIME (buf, sizeof buf, "%Y-%m-%d %H:%M:%S", t);
 206     return buf;
 207 }
 208 
 209 /* --------------------------------------------------------------------------------------------- */
 210 
 211 /**
 212  * Override MC's message().
 213  *
 214  * vfs_parse_ls_lga() calls this on error. Since MC's uses tty/widgets, it
 215  * will crash us. We replace it with a plain version.
 216  */
 217 void
 218 message (int flags, const char *title, const char *text, ...)
     /* [previous][next][first][last][top][bottom][index][help]  */
 219 {
 220     char *p;
 221     va_list ap;
 222 
 223     (void) flags;
 224     (void) title;
 225 
 226     va_start (ap, text);
 227     p = g_strdup_vprintf (text, ap);
 228     va_end (ap);
 229     printf ("message(): vfs_parse_ls_lga(): parsing error at: %s\n", p);
 230     g_free (p);
 231 }
 232 
 233 /* --------------------------------------------------------------------------------------------- */
 234 /**
 235  * YAML output format.
 236  */
 237 /* --------------------------------------------------------------------------------------------- */
 238 
 239 static void
 240 yaml_dump_stbuf (const struct stat *st)
     /* [previous][next][first][last][top][bottom][index][help]  */
 241 {
 242     /* Various casts and flags here were taken/inspired by info_show_info(). */
 243     printf ("  perm: %s\n", string_perm (st->st_mode));
 244     if (!opt_drop_ids)
 245     {
 246         printf ("  uid: %s\n", symbolic_uid (st->st_uid));
 247         printf ("  gid: %s\n", symbolic_gid (st->st_gid));
 248     }
 249     printf ("  size: %" PRIuMAX "\n", (uintmax_t) st->st_size);
 250     printf ("  nlink: %d\n", (int) st->st_nlink);
 251     if (!opt_drop_mtime)
 252         printf ("  mtime: %s\n", string_date (st->st_mtime));
 253 }
 254 
 255 /* --------------------------------------------------------------------------------------------- */
 256 
 257 static void
 258 yaml_dump_string (const char *name, const char *val)
     /* [previous][next][first][last][top][bottom][index][help]  */
 259 {
 260     char *q;
 261 
 262     q = g_shell_quote (val);
 263     printf ("  %s: %s\n", name, q);
 264     g_free (q);
 265 }
 266 
 267 /* --------------------------------------------------------------------------------------------- */
 268 
 269 static void
 270 yaml_dump_record (gboolean success, const char *input_line, const struct stat *st,
     /* [previous][next][first][last][top][bottom][index][help]  */
 271                   const char *filename, const char *linkname)
 272 {
 273     printf ("-\n");             /* Start new item in the list. */
 274 
 275     if (success)
 276     {
 277         if (filename != NULL)
 278             yaml_dump_string ("name", filename);
 279         if (linkname != NULL)
 280             yaml_dump_string ("linkname", linkname);
 281         yaml_dump_stbuf (st);
 282     }
 283     else
 284     {
 285         yaml_dump_string ("cannot parse input line", input_line);
 286     }
 287 }
 288 
 289 /* --------------------------------------------------------------------------------------------- */
 290 /**
 291  * 'ls' output format.
 292  */
 293 /* --------------------------------------------------------------------------------------------- */
 294 
 295 static void
 296 ls_dump_stbuf (const struct stat *st)
     /* [previous][next][first][last][top][bottom][index][help]  */
 297 {
 298     /* Various casts and flags here were taken/inspired by info_show_info(). */
 299     printf ("%s %3d ", string_perm (st->st_mode), (int) st->st_nlink);
 300     if (!opt_drop_ids)
 301     {
 302         printf ("%8s ", symbolic_uid (st->st_uid));
 303         printf ("%8s ", symbolic_gid (st->st_gid));
 304     }
 305     printf ("%10" PRIuMAX " ", (uintmax_t) st->st_size);
 306     if (!opt_drop_mtime)
 307         printf ("%s ", string_date (st->st_mtime));
 308 }
 309 
 310 /* --------------------------------------------------------------------------------------------- */
 311 
 312 static void
 313 ls_dump_record (gboolean success, const char *input_line, const struct stat *st,
     /* [previous][next][first][last][top][bottom][index][help]  */
 314                 const char *filename, const char *linkname)
 315 {
 316     if (success)
 317     {
 318         ls_dump_stbuf (st);
 319         if (filename != NULL)
 320             printf ("%s", filename);
 321         if (linkname != NULL)
 322             printf (" -> %s", linkname);
 323         printf ("\n");
 324     }
 325     else
 326     {
 327         printf ("cannot parse input line: '%s'\n", input_line);
 328     }
 329 }
 330 
 331 /* ------------------------------------------------------------------------------ */
 332 /**
 333  * Main code.
 334  */
 335 /* ------------------------------------------------------------------------------ */
 336 
 337 static void
 338 process_ls_line (const char *line)
     /* [previous][next][first][last][top][bottom][index][help]  */
 339 {
 340     struct stat st;
 341     char *filename, *linkname;
 342     gboolean success;
 343 
 344     memset (&st, 0, sizeof st);
 345     filename = NULL;
 346     linkname = NULL;
 347 
 348     success = vfs_parse_ls_lga (line, &st, &filename, &linkname, NULL);
 349 
 350     if (!success)
 351         error_count++;
 352 
 353     if (opt_output_format == FORMAT_YAML)
 354         yaml_dump_record (success, line, &st, filename, linkname);
 355     else
 356         ls_dump_record (success, line, &st, filename, linkname);
 357 
 358     g_free (filename);
 359     g_free (linkname);
 360 }
 361 
 362 /* ------------------------------------------------------------------------------ */
 363 
 364 static void
 365 process_input (FILE * input)
     /* [previous][next][first][last][top][bottom][index][help]  */
 366 {
 367     char line[BUF_4K];
 368 
 369     while (fgets (line, sizeof line, input) != NULL)
 370     {
 371         chomp (line);           /* Not mandatory. Makes error messages nicer. */
 372         if (strncmp (line, "total ", 6) == 0)   /* Convenience only: makes 'ls -l' parse cleanly. */
 373             continue;
 374         process_ls_line (line);
 375     }
 376 }
 377 
 378 /* ------------------------------------------------------------------------------ */
 379 
 380 int
 381 main (int argc, char *argv[])
     /* [previous][next][first][last][top][bottom][index][help]  */
 382 {
 383     FILE *input;
 384 
 385     if (!parse_command_line (&argc, &argv))
 386         return EXIT_FAILURE;
 387 
 388     if (argc >= 2)
 389     {
 390         input = fopen (argv[1], "r");
 391         if (input == NULL)
 392         {
 393             perror (argv[1]);
 394             return EXIT_FAILURE;
 395         }
 396     }
 397     else
 398     {
 399         input = stdin;
 400     }
 401 
 402     process_input (input);
 403 
 404     return (error_count > 0) ? EXIT_FAILURE : EXIT_SUCCESS;
 405 }
 406 
 407 /* ------------------------------------------------------------------------------ */

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