summaryrefslogtreecommitdiffhomepage
path: root/libbb/getopt_ulflags.c
blob: 39a7d1d29a3a9b57a3ae0e8c463bbab9cf5a9500 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
/* vi: set sw=4 ts=4: */
/*
 * universal getopt_ulflags implementation for busybox
 *
 * Copyright (C) 2003  Vladimir Oleynik  <dzo@simtreas.ru>
 *
 * 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 2 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, write to the Free Software
 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
 *
 */

#include <getopt.h>
#include <string.h>
#include <assert.h>
#include <stdlib.h>
#include "libbb.h"

/*
You can set bb_opt_complementaly as string with one or more
complementaly or incongruously options.
If sequential founded option haved from this string
then your incongruously pairs unsets and complementaly make add sets.
Format:
one char - option for check,
chars - complementaly option for add sets.
- chars - option triggered for unsets.
~ chars - option incongruously.
*       - option list, called add_to_list(*ptr_from_usaged, optarg)
:       - separator.
Example: du applet can have options "-s" and "-d size"
If getopt found -s then -d option flag unset or if found -d then -s unset.
For this result you must set bb_opt_complementaly = "s-d:d-s".
Result have last option flag only from called arguments.
Warning! You can check returned flag, pointer to "d:" argument seted
to own optarg always.
Example two: cut applet must only one type of list may be specified,
and -b, -c and -f incongruously option, overwited option is error also.
You must set bb_opt_complementaly = "b~cf:c~bf:f~bc".
If called have more one specified, return value have error flag -
high bite set (0x80000000UL).
Example three: grep applet can have one or more "-e pattern" arguments.
You should use bb_getopt_ulflags() as
llist_t *paterns;
bb_opt_complementaly = "e*";
bb_getopt_ulflags (argc, argv, "e:", &paterns);
*/

const char *bb_opt_complementaly;

typedef struct
{
	unsigned char opt;
	char list_flg;
	unsigned long switch_on;
	unsigned long switch_off;
	unsigned long incongruously;
	void **optarg;              /* char **optarg or llist_t **optarg */
} t_complementaly;

/* You can set bb_applet_long_options for parse called long options */

static const struct option bb_default_long_options[] = {
     /*   { "help", 0, NULL, '?' }, */
	{ 0, 0, 0, 0 }
};

const struct option *bb_applet_long_options = bb_default_long_options;


unsigned long
bb_getopt_ulflags (int argc, char **argv, const char *applet_opts, ...)
{
  unsigned long flags = 0;
  t_complementaly complementaly[sizeof(flags) * 8 + 1];
  int c;
  const unsigned char *s;
  t_complementaly *on_off;
  va_list p;

  va_start (p, applet_opts);

  /* skip GNU extension */
  s = applet_opts;
  if(*s == '+' || *s == '-')
	s++;

  c = 0;
  on_off = complementaly;
  for (; *s; s++) {
	if(c >= (sizeof(flags)*8))
		break;
	on_off->opt = *s;
	on_off->switch_on = (1 << c);
	on_off->list_flg = 0;
	on_off->switch_off = 0;
	on_off->incongruously = 0;
	on_off->optarg = NULL;
	if (s[1] == ':') {
	  on_off->optarg = va_arg (p, void **);
	  do
		s++;
	  while (s[1] == ':');
	}
	on_off++;
	c++;
  }
  on_off->opt = 0;
  c = 0;
  for (s = bb_opt_complementaly; s && *s; s++) {
    t_complementaly *pair;

    if (*s == ':') {
	  c = 0;
	  continue;
    }
    if (c)
	  continue;
    for (on_off = complementaly; on_off->opt; on_off++)
	  if (on_off->opt == *s)
	    break;
    pair = on_off;
    for(s++; *s && *s != ':'; s++) {
      if (*s == '-' || *s == '~') {
	  c = *s;
      } else if(*s == '*') {
	pair->list_flg++;
      } else {
	  unsigned long *pair_switch = &(pair->switch_on);

	  if(c)
	    pair_switch = c == '-' ? &(pair->switch_off) : &(pair->incongruously);
	  for (on_off = complementaly; on_off->opt; on_off++)
	    if (on_off->opt == *s) {
		  *pair_switch |= on_off->switch_on;
		  break;
	    }
      }
    }
    s--;
  }

  while ((c = getopt_long (argc, argv, applet_opts,
			    bb_applet_long_options, NULL)) > 0) {
	for (on_off = complementaly; on_off->opt != c; on_off++) {
	    if(!on_off->opt)
			bb_show_usage ();
	}
	if(flags & on_off->incongruously)
	    flags |= 0x80000000UL;
	flags &= ~on_off->switch_off;
	flags |= on_off->switch_on;
	if(on_off->list_flg) {
	   *(llist_t **)(on_off->optarg) =
		llist_add_to(*(llist_t **)(on_off->optarg), optarg);
	} else if (on_off->optarg) {
	    *(char **)(on_off->optarg) = optarg;
	}
  }
  return flags;
}