#include "options.h"
#define OPTIONS_STRING "cmuo:T:z:dfiMnrbt:k:S:0123456789"
#define OLD_SPEC_OPTIONS_STRING "bdfiMnrcmuo:T:z:t:k:S:"
#define OPTIONS_OLDSPEC 0x1
#define OPTIONS_STARTSPEC 0x2
static int
is_number(char *C)
{
size_t i;
for (i = 0; i < strlen(C); i++)
if (!isdigit((uchar_t)C[i]))
return (0);
return (1);
}
static int
field_spec_has_modifiers(char *C, int length)
{
int p_nonmodifiers = strspn(C, ",.1234567890");
if (p_nonmodifiers == length)
return (0);
return (1);
}
static void
field_apply_all(field_t *fc, flag_t flags)
{
field_t *f;
for (f = fc; f; f = f->f_next)
if ((f->f_options & FIELD_MODIFIERS_DEFINED) == 0)
f->f_options |= flags;
}
static int
parse_field_spec(field_t *F, char *C, int flags, int length)
{
int p_period = MIN(length, strcspn(C, "."));
int p_modifiers = MIN(length, strspn(C, ".1234567890"));
int p_boundary = MIN(p_period, p_modifiers);
int field = 0;
int offset = 0;
int offset_seen = 0;
int i;
int blanks_flag = 0;
for (i = 0; i < p_boundary; i++) {
if (isdigit((uchar_t)C[i]))
field = (10 * field) + (C[i] - '0');
else
return (1);
}
if (p_period < p_modifiers) {
for (i = p_period + 1; i < p_modifiers; i++) {
if (isdigit((uchar_t)C[i])) {
offset_seen++;
offset = (10 * offset) + (C[i] - '0');
} else {
return (1);
}
}
}
if (p_modifiers < length) {
for (i = p_modifiers; i < length; i++) {
switch (C[i]) {
case 'b':
blanks_flag = 1;
break;
case 'd':
F->f_options |= FIELD_DICTIONARY_ORDER;
break;
case 'f':
F->f_options |= FIELD_FOLD_UPPERCASE;
break;
case 'i':
F->f_options |=
FIELD_IGNORE_NONPRINTABLES;
break;
case 'M':
F->f_species = MONTH;
break;
case 'n':
F->f_species = NUMERIC;
break;
case 'r':
F->f_options |=
FIELD_REVERSE_COMPARISONS;
break;
default:
usage();
break;
}
}
}
if (flags & OPTIONS_STARTSPEC) {
F->f_start_field = field;
F->f_start_offset = offset;
if ((flags & OPTIONS_OLDSPEC) != OPTIONS_OLDSPEC) {
F->f_start_field--;
if (offset_seen)
F->f_start_offset--;
}
F->f_options |= blanks_flag ? FIELD_IGNORE_BLANKS_START : 0;
} else {
F->f_end_field = field;
F->f_end_offset = offset;
if ((flags & OPTIONS_OLDSPEC) != OPTIONS_OLDSPEC &&
offset_seen && offset != 0)
F->f_end_field--;
F->f_options |= blanks_flag ? FIELD_IGNORE_BLANKS_END : 0;
}
return (0);
}
static void
parse_new_field_spec(sort_t *S, char *arg)
{
int length = strlen(arg);
int p_comma = MIN(length, strcspn(arg, ","));
field_t *nF;
int p;
if (field_spec_has_modifiers(arg, length)) {
nF = field_new(NULL);
nF->f_options = FIELD_MODIFIERS_DEFINED;
} else {
nF = field_new(S);
}
p = parse_field_spec(nF, arg, OPTIONS_STARTSPEC, p_comma);
if (p != 0)
usage();
if (p_comma < length) {
p = parse_field_spec(nF, &(arg[p_comma + 1]), 0,
strlen(&(arg[p_comma + 1])));
if (p != 0)
usage();
}
if (nF->f_start_field < 0 || nF->f_start_offset < 0) {
if (S->m_verbose)
warn("-k %s is not a supported field specifier\n", arg);
}
nF->f_start_field = MAX(nF->f_start_field, 0);
nF->f_start_offset = MAX(nF->f_start_offset, 0);
if (nF->f_end_field == -1 || nF->f_start_field < nF->f_end_field ||
(nF->f_start_field == nF->f_end_field &&
nF->f_start_offset < nF->f_end_offset)) {
field_add_to_chain(&(S->m_fields_head), nF);
} else if (S->m_verbose) {
warn("illegal field -k %s omitted", arg);
}
}
static int
parse_old_field_spec(sort_t *S, int argc, char *argv[])
{
field_t *nF;
int c, p;
char *arg = argv[optind];
if (field_spec_has_modifiers(arg + 1, strlen(arg + 1))) {
nF = field_new(NULL);
nF->f_options = FIELD_MODIFIERS_DEFINED;
} else {
nF = field_new(S);
}
p = parse_field_spec(nF, arg + 1, OPTIONS_OLDSPEC | OPTIONS_STARTSPEC,
strlen(arg + 1));
if (p != 0) {
field_delete(nF);
return (0);
}
for (arg = argv[++optind]; optind < argc; arg = argv[optind]) {
if (strlen(arg) >= 2 && *arg == '-' &&
isdigit(*(uchar_t *)(arg + 1))) {
(void) parse_field_spec(nF, arg + 1,
OPTIONS_OLDSPEC, strlen(arg) - 1);
field_add_to_chain(&(S->m_fields_head), nF);
optind++;
return (1);
}
if ((c = getopt(argc, argv, OLD_SPEC_OPTIONS_STRING)) != EOF) {
switch (c) {
case 'b':
nF->f_options |= FIELD_IGNORE_BLANKS_START;
break;
case 'd':
nF->f_options |= FIELD_DICTIONARY_ORDER;
break;
case 'f':
nF->f_options |= FIELD_FOLD_UPPERCASE;
break;
case 'i':
nF->f_options |= FIELD_IGNORE_NONPRINTABLES;
break;
case 'M':
nF->f_species = MONTH;
break;
case 'n':
nF->f_species = NUMERIC;
break;
case 'r':
nF->f_options |= FIELD_REVERSE_COMPARISONS;
break;
case '?':
case 'c':
case 'm':
case 'u':
optind -= 1;
field_add_to_chain(&(S->m_fields_head), nF);
return (1);
case 'o':
case 'T':
case 'z':
case 't':
case 'k':
case 'S':
if (optarg == argv[optind - 1] + 2) {
optind -= 1;
} else {
optind -= 2;
}
field_add_to_chain(&(S->m_fields_head), nF);
return (1);
default:
die(EMSG_UNKN_OPTION);
}
} else {
break;
}
}
field_add_to_chain(&(S->m_fields_head), nF);
return (1);
}
int
options(sort_t *S, int argc, char *argv[])
{
int c;
optind = 1;
while (optind < argc) {
if (strncmp("-y", argv[optind], strlen("-y")) == 0) {
if (strlen(argv[optind]) == strlen("-y") &&
optind + 1 < argc &&
is_number(argv[optind + 1]))
optind += 2;
else
optind += 1;
}
if ((c = getopt(argc, argv, OPTIONS_STRING)) != EOF) {
switch (c) {
case 'c':
S->m_check_if_sorted_only = 1;
break;
case 'm':
S->m_merge_only = 1;
break;
case 'u':
S->m_unique_lines = 1;
break;
case 'o':
S->m_output_filename = optarg;
break;
case 'T':
S->m_tmpdir_template = optarg;
break;
case 'z':
break;
case 'd':
S->m_field_options |= FIELD_DICTIONARY_ORDER;
field_apply_all(S->m_fields_head,
FIELD_DICTIONARY_ORDER);
break;
case 'f':
S->m_field_options |= FIELD_FOLD_UPPERCASE;
field_apply_all(S->m_fields_head,
FIELD_FOLD_UPPERCASE);
break;
case 'i':
S->m_field_options |=
FIELD_IGNORE_NONPRINTABLES;
field_apply_all(S->m_fields_head,
FIELD_IGNORE_NONPRINTABLES);
break;
case 'M':
S->m_default_species = MONTH;
S->m_field_options &=
~FIELD_IGNORE_BLANKS_START;
break;
case 'n':
S->m_default_species = NUMERIC;
{
field_t *f;
for (f = S->m_fields_head; f;
f = f->f_next)
if ((f->f_options &
FIELD_MODIFIERS_DEFINED) ==
0)
f->f_species = NUMERIC;
}
break;
case 'b':
S->m_field_options |=
FIELD_IGNORE_BLANKS_START |
FIELD_IGNORE_BLANKS_END;
break;
case 'r':
S->m_field_options |=
FIELD_REVERSE_COMPARISONS;
field_apply_all(S->m_fields_head,
FIELD_REVERSE_COMPARISONS);
break;
case 't':
if (S->m_single_byte_locale) {
S->m_field_separator.sc =
#ifdef DEBUG
xstreql(optarg, "\\t") ? '\t' :
#endif
optarg[0];
} else
(void) mbtowc(&S->m_field_separator.wc,
optarg, MB_CUR_MAX);
break;
case 'k':
(void) parse_new_field_spec(S, optarg);
break;
case 'S':
S->m_memory_limit = strtomem(optarg);
#ifdef DEBUG
(void) fprintf(stderr, CMDNAME
": limiting size to %d bytes\n",
S->m_memory_limit);
#endif
break;
case '0':
case '1':
case '2':
case '3':
case '4':
case '5':
case '6':
case '7':
case '8':
case '9':
usage();
break;
case '?':
usage();
break;
}
continue;
}
if (xstreql(argv[optind - 1], "--")) {
while (optind < argc) {
if (xstreql(argv[optind], "-"))
S->m_input_from_stdin = 1;
else
stream_add_file_to_chain(
&(S->m_input_streams),
argv[optind]);
optind++;
}
break;
}
if (optind < argc) {
if (xstreql(argv[optind], "-")) {
S->m_input_from_stdin = 1;
optind++;
} else if (*(argv[optind]) != '+' ||
!parse_old_field_spec(S, argc, argv)) {
stream_add_file_to_chain(&(S->m_input_streams),
argv[optind]);
optind++;
}
}
}
if (S->m_input_streams == NULL)
S->m_input_from_stdin = 1;
if (S->m_output_filename == NULL)
S->m_output_to_stdout = 1;
if (S->m_fields_head == NULL) {
S->m_field_options &= ~FIELD_IGNORE_BLANKS_START;
(void) parse_new_field_spec(S, "1");
if (S->m_field_separator.wc == 0 &&
S->m_default_species == ALPHA &&
S->m_field_options == 0)
S->m_entire_line = 1;
}
return (0);
}