319 lines
9.4 KiB
C
319 lines
9.4 KiB
C
/* recset.c - Set and update the values of fields. */
|
|
|
|
/* Copyright (C) 2010-2022 Jose E. Marchesi */
|
|
|
|
/* 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 3 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 <config.h>
|
|
|
|
#include <getopt.h>
|
|
#include <string.h>
|
|
#include <stdlib.h>
|
|
#include <xalloc.h>
|
|
#include <gettext.h>
|
|
#define _(str) gettext (str)
|
|
|
|
#include <rec.h>
|
|
#include <recutl.h>
|
|
|
|
/*
|
|
* Global variables.
|
|
*/
|
|
|
|
char *recutl_sex_str = NULL;
|
|
rec_sex_t recutl_sex = NULL;
|
|
char *recutl_quick_str = NULL;
|
|
char *recutl_fex_str = NULL;
|
|
rec_fex_t recutl_fex = NULL;
|
|
char *recutl_type = NULL;
|
|
int recset_action = REC_SET_ACT_NONE;
|
|
char *recset_value = NULL;
|
|
char *recset_new_field_name = NULL;
|
|
bool recutl_insensitive = false;
|
|
char *recset_file = NULL;
|
|
bool recset_force = false;
|
|
bool recset_verbose = false;
|
|
bool recset_external = true;
|
|
bool recset_descriptor_renamed = false;
|
|
size_t recutl_random = 0;
|
|
|
|
/*
|
|
* Command line options management
|
|
*/
|
|
|
|
enum
|
|
{
|
|
COMMON_ARGS,
|
|
RECORD_SELECTION_ARGS,
|
|
FIELD_EXPR_ARG,
|
|
ADD_ACTION_ARG,
|
|
RENAME_ACTION_ARG,
|
|
DELETE_ACTION_ARG,
|
|
COMMENT_ACTION_ARG,
|
|
SET_ACTION_ARG,
|
|
SET_ADD_ACTION_ARG,
|
|
FORCE_ARG,
|
|
VERBOSE_ARG,
|
|
NO_EXTERNAL_ARG
|
|
};
|
|
|
|
static const struct option GNU_longOptions[] =
|
|
{
|
|
COMMON_LONG_ARGS,
|
|
RECORD_SELECTION_LONG_ARGS,
|
|
{"fields", required_argument, NULL, FIELD_EXPR_ARG},
|
|
{"add", required_argument, NULL, ADD_ACTION_ARG},
|
|
{"rename", required_argument, NULL, RENAME_ACTION_ARG},
|
|
{"delete", no_argument, NULL, DELETE_ACTION_ARG},
|
|
{"comment", no_argument, NULL, COMMENT_ACTION_ARG},
|
|
{"set", required_argument, NULL, SET_ACTION_ARG},
|
|
{"set-add", required_argument, NULL, SET_ADD_ACTION_ARG},
|
|
{"force", no_argument, NULL, FORCE_ARG},
|
|
{"verbose", no_argument, NULL, VERBOSE_ARG},
|
|
{"no-external", no_argument, NULL, NO_EXTERNAL_ARG},
|
|
{NULL, 0, NULL, 0}
|
|
};
|
|
|
|
/*
|
|
* Functions.
|
|
*/
|
|
|
|
|
|
void
|
|
recutl_print_help (void)
|
|
{
|
|
/* TRANSLATORS: --help output, recset synopsis.
|
|
no-wrap */
|
|
printf (_("\
|
|
Usage: recset [OPTION]... [FILE]...\n"));
|
|
|
|
/* TRANSLATORS: --help output, recset short description.
|
|
no-wrap */
|
|
fputs (_("\
|
|
Alter or delete fields in records.\n"), stdout);
|
|
|
|
puts ("");
|
|
/* TRANSLATORS: --help output, recset options.
|
|
no-wrap */
|
|
fputs (_("\
|
|
--no-external don't use external descriptors.\n\
|
|
--force alter the records even if violating record\n\
|
|
restrictions.\n"), stdout);
|
|
|
|
recutl_print_help_common ();
|
|
|
|
puts ("");
|
|
recutl_print_help_record_selection ();
|
|
|
|
puts ("");
|
|
/* TRANSLATORS: --help output, recset field selection options.
|
|
no-wrap */
|
|
fputs (_("\
|
|
Field selection options:\n\
|
|
-f, --fields=FIELDS comma-separated list of field names with optional\n\
|
|
subscripts.\n"), stdout);
|
|
|
|
puts ("");
|
|
/* TRANSLATORS: --help output, recset actions.
|
|
no-wrap */
|
|
fputs (_("\
|
|
Actions:\n\
|
|
-s, --set=VALUE change the value of the selected fields.\n\
|
|
-a, --add=VALUE add the selected fields with the given value.\n\
|
|
-S, --set-add=VALUE change the value of the selected fields. If they don't\n\
|
|
exist then add a new field with that value.\n\
|
|
-r, --rename=NAME rename the selected fields to a given name. If an entire\n\
|
|
record set is selected then the field is renamed in the\n\
|
|
record descriptor as well.\n\
|
|
-d, --delete delete the selected fields.\n\
|
|
-c, --comment comment out the selected fields.\n"), stdout);
|
|
|
|
puts ("");
|
|
recutl_print_help_footer ();
|
|
}
|
|
|
|
/* This macro is used in recset_parse_args to avoid code repetition in
|
|
the actions (set, add, delete, ...) switch cases.. */
|
|
#define CHECK_ACTION_PREREQ \
|
|
do \
|
|
{ \
|
|
if (!recutl_fex) \
|
|
{ \
|
|
recutl_fatal (_("please specify some field with -f.\n")); \
|
|
} \
|
|
\
|
|
if (recset_action != REC_SET_ACT_NONE) \
|
|
{ \
|
|
recutl_fatal (_("please specify just one action.\n")); \
|
|
} \
|
|
} \
|
|
while (0)
|
|
|
|
static void
|
|
recset_parse_args (int argc,
|
|
char **argv)
|
|
{
|
|
int ret;
|
|
char c;
|
|
|
|
while ((ret = getopt_long (argc,
|
|
argv,
|
|
RECORD_SELECTION_SHORT_ARGS
|
|
"dct:s:S:a:f:r:",
|
|
GNU_longOptions,
|
|
NULL)) != -1)
|
|
{
|
|
c = ret;
|
|
switch (c)
|
|
{
|
|
COMMON_ARGS_CASES
|
|
RECORD_SELECTION_ARGS_CASES
|
|
case FORCE_ARG:
|
|
recset_force = true;
|
|
break;
|
|
case VERBOSE_ARG:
|
|
recset_verbose = true;
|
|
break;
|
|
case FIELD_EXPR_ARG:
|
|
case 'f':
|
|
recutl_fex_str = xstrdup (optarg);
|
|
if (!rec_fex_check (recutl_fex_str, REC_FEX_SUBSCRIPTS))
|
|
recutl_fatal (_("invalid field expression in -f.\n"));
|
|
|
|
/* Create the field expression. */
|
|
recutl_fex = rec_fex_new (recutl_fex_str,
|
|
REC_FEX_SUBSCRIPTS);
|
|
if (!recutl_fex)
|
|
recutl_fatal (_("creating the field expression.\n"));
|
|
|
|
/* Sort it. */
|
|
rec_fex_sort (recutl_fex);
|
|
break;
|
|
case SET_ACTION_ARG:
|
|
case 's':
|
|
CHECK_ACTION_PREREQ;
|
|
recset_action = REC_SET_ACT_SET;
|
|
recset_value = xstrdup (optarg);
|
|
break;
|
|
case RENAME_ACTION_ARG:
|
|
case 'r':
|
|
CHECK_ACTION_PREREQ;
|
|
if (rec_fex_size (recutl_fex) != 1)
|
|
recutl_fatal (_("the rename operation requires just one\
|
|
field with an optional subscript.\n"));
|
|
|
|
recset_action = REC_SET_ACT_RENAME;
|
|
recset_value = xstrdup (optarg);
|
|
|
|
/* Validate the new name. */
|
|
recset_new_field_name = recset_value;
|
|
if (!rec_field_name_p (recset_new_field_name))
|
|
recutl_fatal (_("invalid field name %s.\n"), recset_value);
|
|
break;
|
|
case ADD_ACTION_ARG:
|
|
case 'a':
|
|
CHECK_ACTION_PREREQ;
|
|
recset_action = REC_SET_ACT_ADD;
|
|
recset_value = xstrdup (optarg);
|
|
break;
|
|
case SET_ADD_ACTION_ARG:
|
|
case 'S':
|
|
CHECK_ACTION_PREREQ;
|
|
recset_action = REC_SET_ACT_SETADD;
|
|
recset_value = xstrdup (optarg);
|
|
break;
|
|
case DELETE_ACTION_ARG:
|
|
case 'd':
|
|
CHECK_ACTION_PREREQ;
|
|
recset_action = REC_SET_ACT_DELETE;
|
|
break;
|
|
case COMMENT_ACTION_ARG:
|
|
case 'c':
|
|
CHECK_ACTION_PREREQ;
|
|
recset_action = REC_SET_ACT_COMMENT;
|
|
break;
|
|
case NO_EXTERNAL_ARG:
|
|
recset_external = false;
|
|
break;
|
|
default:
|
|
exit (EXIT_FAILURE);
|
|
break;
|
|
}
|
|
}
|
|
|
|
/* Read the name of the data source. */
|
|
if (optind < argc)
|
|
{
|
|
if ((argc - optind) != 1)
|
|
{
|
|
recutl_print_help ();
|
|
exit (EXIT_FAILURE);
|
|
}
|
|
|
|
recset_file = argv[optind++];
|
|
}
|
|
}
|
|
|
|
static void
|
|
recset_process_actions (rec_db_t db)
|
|
{
|
|
int flags = 0;
|
|
|
|
if (recutl_insensitive)
|
|
flags = flags | REC_F_ICASE;
|
|
|
|
if (!rec_db_set (db,
|
|
recutl_type,
|
|
recutl_index (),
|
|
recutl_sex,
|
|
recutl_quick_str,
|
|
recutl_random,
|
|
recutl_fex,
|
|
recset_action,
|
|
recset_value,
|
|
flags))
|
|
recutl_out_of_memory ();
|
|
|
|
/* Check the integrity of the resulting database. */
|
|
if (!recset_force && db)
|
|
recutl_check_integrity (db, recset_verbose, recset_external);
|
|
}
|
|
|
|
int
|
|
main (int argc, char *argv[])
|
|
{
|
|
rec_db_t db;
|
|
|
|
recutl_init ("recset");
|
|
|
|
/* Parse arguments. */
|
|
recset_parse_args (argc, argv);
|
|
|
|
db = recutl_read_db_from_file (recset_file);
|
|
if (!db)
|
|
recutl_fatal (_("cannot read file %s\n"), recset_file);
|
|
|
|
recset_process_actions (db);
|
|
|
|
if (!recutl_file_is_writable (recset_file))
|
|
{
|
|
recutl_error (_("file %s is not writable.\n"), recset_file);
|
|
return EXIT_FAILURE;
|
|
}
|
|
recutl_write_db_to_file (db, recset_file);
|
|
|
|
return EXIT_SUCCESS;
|
|
}
|