Ver código fonte

[parseopt] Add generic option-parsing library

Command implementations tend to include a substantial amount of common
boilerplate code revolving around the parsing of command-line options
and arguments.  This increases the size cost of each command.

Introduce an option-parsing library that abstracts out the common
operations involved in command implementations.  This enables the size
of each individual command to be reduced, and also enhances
consistency between commands.

Total size of the library is 704 bytes, to be amortised across all
command implementations.

Signed-off-by: Michael Brown <mcb30@ipxe.org>
tags/v1.20.1
Michael Brown 13 anos atrás
pai
commit
216fd0a5cf
3 arquivos alterados com 347 adições e 0 exclusões
  1. 219
    0
      src/core/parseopt.c
  2. 1
    0
      src/include/ipxe/errfile.h
  3. 127
    0
      src/include/ipxe/parseopt.h

+ 219
- 0
src/core/parseopt.c Ver arquivo

@@ -0,0 +1,219 @@
1
+/*
2
+ * Copyright (C) 2010 Michael Brown <mbrown@fensystems.co.uk>.
3
+ *
4
+ * This program is free software; you can redistribute it and/or
5
+ * modify it under the terms of the GNU General Public License as
6
+ * published by the Free Software Foundation; either version 2 of the
7
+ * License, or any later version.
8
+ *
9
+ * This program is distributed in the hope that it will be useful, but
10
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
11
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
12
+ * General Public License for more details.
13
+ *
14
+ * You should have received a copy of the GNU General Public License
15
+ * along with this program; if not, write to the Free Software
16
+ * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
17
+ */
18
+
19
+FILE_LICENCE ( GPL2_OR_LATER );
20
+
21
+#include <stddef.h>
22
+#include <stdint.h>
23
+#include <stdlib.h>
24
+#include <stdio.h>
25
+#include <string.h>
26
+#include <errno.h>
27
+#include <getopt.h>
28
+#include <ipxe/netdevice.h>
29
+#include <ipxe/image.h>
30
+#include <ipxe/parseopt.h>
31
+
32
+/** @file
33
+ *
34
+ * Command line option parsing
35
+ *
36
+ */
37
+
38
+/** Return status code for "--help" option */
39
+#define ECANCELED_NO_OP __einfo_error ( EINFO_ECANCELED_NO_OP )
40
+#define EINFO_ECANCELED_NO_OP \
41
+	__einfo_uniqify ( EINFO_ECANCELED, 0x01, "Nothing to do" )
42
+
43
+/**
44
+* Parse string value
45
+ *
46
+ * @v text		Text
47
+ * @ret value		String value
48
+ * @ret rc		Return status code
49
+ */
50
+int parse_string ( const char *text, const char **value ) {
51
+
52
+	/* Sanity check */
53
+	assert ( text != NULL );
54
+
55
+	/* Parse string */
56
+	*value = text;
57
+
58
+	return 0;
59
+}
60
+
61
+/**
62
+ * Parse integer value
63
+ *
64
+ * @v text		Text
65
+ * @ret value		Integer value
66
+ * @ret rc		Return status code
67
+ */
68
+int parse_integer ( const char *text, unsigned int *value ) {
69
+	char *endp;
70
+
71
+	/* Sanity check */
72
+	assert ( text != NULL );
73
+
74
+	/* Parse integer */
75
+	*value = strtoul ( text, &endp, 10 );
76
+	if ( *endp ) {
77
+		printf ( "\"%s\": invalid integer value\n", text );
78
+		return -EINVAL;
79
+	}
80
+
81
+	return 0;
82
+}
83
+
84
+/**
85
+ * Parse network device name
86
+ *
87
+ * @v text		Text
88
+ * @ret netdev		Network device
89
+ * @ret rc		Return status code
90
+ */
91
+int parse_netdev ( const char *text, struct net_device **netdev ) {
92
+
93
+	/* Sanity check */
94
+	assert ( text != NULL );
95
+
96
+	/* Find network device */
97
+	*netdev = find_netdev ( text );
98
+	if ( ! *netdev ) {
99
+		printf ( "\"%s\": no such network device\n", text );
100
+		return -ENODEV;
101
+	}
102
+
103
+	return 0;
104
+}
105
+
106
+/**
107
+ * Parse image name
108
+ *
109
+ * @v text		Text
110
+ * @ret image		Image
111
+ * @ret rc		Return status code
112
+ */
113
+int parse_image ( const char *text, struct image **image ) {
114
+
115
+	/* Sanity check */
116
+	assert ( text != NULL );
117
+
118
+	/* Find network device */
119
+	*image = find_image ( text );
120
+	if ( ! *image ) {
121
+		printf ( "\"%s\": no such image\n", text );
122
+		return -ENOENT;
123
+	}
124
+
125
+	return 0;
126
+}
127
+
128
+/**
129
+ * Print command usage message
130
+ *
131
+ * @v cmd		Command descriptor
132
+ * @v argv		Argument list
133
+ */
134
+void print_usage ( struct command_descriptor *cmd, char **argv ) {
135
+	printf ( "Usage:\n\n  %s %s\n", argv[0], cmd->usage_description );
136
+}
137
+
138
+/**
139
+ * Parse command-line options
140
+ *
141
+ * @v argc		Argument count
142
+ * @v argv		Argument list
143
+ * @v cmd		Command descriptor
144
+ * @v opts		Options
145
+ * @ret rc		Return status code
146
+ */
147
+int parse_options ( int argc, char **argv, struct command_descriptor *cmd,
148
+		    void *opts ) {
149
+	struct option longopts[ cmd->num_options + 1 /* help */ + 1 /* end */ ];
150
+	char shortopts[ cmd->num_options * 3 /* possible "::" */ + 1 /* "h" */
151
+			+ 1 /* NUL */ ];
152
+	unsigned int shortopt_idx = 0;
153
+	int ( * parse ) ( const char *text, void *value );
154
+	void *value;
155
+	unsigned int i;
156
+	unsigned int j;
157
+	unsigned int num_args;
158
+	int c;
159
+	int rc;
160
+
161
+	/* Construct long and short option lists for getopt_long() */
162
+	memset ( longopts, 0, sizeof ( longopts ) );
163
+	for ( i = 0 ; i < cmd->num_options ; i++ ) {
164
+		longopts[i].name = cmd->options[i].longopt;
165
+		longopts[i].has_arg = cmd->options[i].has_arg;
166
+		longopts[i].val = cmd->options[i].shortopt;
167
+		shortopts[shortopt_idx++] = cmd->options[i].shortopt;
168
+		assert ( cmd->options[i].has_arg <= optional_argument );
169
+		for ( j = cmd->options[i].has_arg ; j > 0 ; j-- )
170
+			shortopts[shortopt_idx++] = ':';
171
+	}
172
+	longopts[i].name = "help";
173
+	longopts[i].val = 'h';
174
+	shortopts[shortopt_idx++] = 'h';
175
+	shortopts[shortopt_idx++] = '\0';
176
+	assert ( shortopt_idx <= sizeof ( shortopts ) );
177
+	DBGC ( cmd,  "Command \"%s\" has options \"%s\", %d-%d args, len %d\n",
178
+	       argv[0], shortopts, cmd->min_args, cmd->max_args, cmd->len );
179
+
180
+	/* Clear options */
181
+	memset ( opts, 0, cmd->len );
182
+
183
+	/* Parse options */
184
+	while ( ( c = getopt_long ( argc, argv, shortopts, longopts,
185
+				    NULL ) ) >= 0 ) {
186
+		switch ( c ) {
187
+		case 'h' :
188
+			/* Print help */
189
+			print_usage ( cmd, argv );
190
+			return -ECANCELED_NO_OP;
191
+		case '?' :
192
+		case ':' :
193
+			/* Print usage message */
194
+			print_usage ( cmd, argv );
195
+			return -EINVAL;
196
+		default:
197
+			/* Search for an option to parse */
198
+			for ( i = 0 ; i < cmd->num_options ; i++ ) {
199
+				if ( c != cmd->options[i].shortopt )
200
+					continue;
201
+				parse = cmd->options[i].parse;
202
+				value = ( opts + cmd->options[i].offset );
203
+				if ( ( rc = parse ( optarg, value ) ) != 0 )
204
+					return rc;
205
+				break;
206
+			}
207
+			assert ( i < cmd->num_options );
208
+		}
209
+	}
210
+
211
+	/* Check remaining arguments */
212
+	num_args = ( argc - optind );
213
+	if ( ( num_args < cmd->min_args ) || ( num_args > cmd->max_args ) ) {
214
+		print_usage ( cmd, argv );
215
+		return -ERANGE;
216
+	}
217
+
218
+	return 0;
219
+}

+ 1
- 0
src/include/ipxe/errfile.h Ver arquivo

@@ -59,6 +59,7 @@ FILE_LICENCE ( GPL2_OR_LATER );
59 59
 #define ERRFILE_acpi		       ( ERRFILE_CORE | 0x00130000 )
60 60
 #define ERRFILE_null_sanboot	       ( ERRFILE_CORE | 0x00140000 )
61 61
 #define ERRFILE_edd		       ( ERRFILE_CORE | 0x00150000 )
62
+#define ERRFILE_parseopt	       ( ERRFILE_CORE | 0x00160000 )
62 63
 
63 64
 #define ERRFILE_eisa		     ( ERRFILE_DRIVER | 0x00000000 )
64 65
 #define ERRFILE_isa		     ( ERRFILE_DRIVER | 0x00010000 )

+ 127
- 0
src/include/ipxe/parseopt.h Ver arquivo

@@ -0,0 +1,127 @@
1
+#ifndef _IPXE_PARSEOPT_H
2
+#define _IPXE_PARSEOPT_H
3
+
4
+/** @file
5
+ *
6
+ * Command line option parsing
7
+ *
8
+ */
9
+
10
+FILE_LICENCE ( GPL2_OR_LATER );
11
+
12
+#include <stdint.h>
13
+#include <stddef.h>
14
+
15
+struct net_device;
16
+struct image;
17
+
18
+/** A command-line option descriptor */
19
+struct option_descriptor {
20
+	/** Long option name, if any */
21
+	const char *longopt;
22
+	/** Short option name */
23
+	char shortopt;
24
+	/** Argument requirement (as for @c struct @c option) */
25
+	uint8_t has_arg;
26
+	/** Offset of field within options structure */
27
+	uint16_t offset;
28
+	/** Parse option
29
+	 *
30
+	 * @v text		Option text
31
+	 * @v value		Option value to fill in
32
+	 * @ret rc		Return status code
33
+	 */
34
+	int ( * parse ) ( const char *text, void *value );
35
+};
36
+
37
+/**
38
+ * Construct option parser
39
+ *
40
+ * @v _struct		Options structure type
41
+ * @v _field		Field within options structure
42
+ * @v _parse		Field type-specific option parser
43
+ * @ret _parse		Generic option parser
44
+ */
45
+#define OPTION_PARSER( _struct, _field, _parse )			      \
46
+	( ( int ( * ) ( const char *text, void *value ) )		      \
47
+	  ( ( ( ( typeof ( _parse ) * ) NULL ) ==			      \
48
+	      ( ( int ( * ) ( const char *text,				      \
49
+			      typeof ( ( ( _struct * ) NULL )->_field ) * ) ) \
50
+		NULL ) ) ? _parse : _parse ) )
51
+
52
+/**
53
+ * Construct option descriptor
54
+ *
55
+ * @v _longopt		Long option name, if any
56
+ * @v _shortopt		Short option name, if any
57
+ * @v _has_arg		Argument requirement
58
+ * @v _struct		Options structure type
59
+ * @v _field		Field within options structure
60
+ * @v _parse		Field type-specific option parser
61
+ * @ret _option		Option descriptor
62
+ */
63
+#define OPTION_DESC( _longopt, _shortopt, _has_arg, _struct, _field, _parse ) \
64
+	{								      \
65
+		.longopt = _longopt,					      \
66
+		.shortopt = _shortopt,					      \
67
+		.has_arg = _has_arg,					      \
68
+		.offset = offsetof ( _struct, _field ),			      \
69
+		.parse = OPTION_PARSER ( _struct, _field, _parse ),	      \
70
+	}
71
+
72
+/** A command descriptor */
73
+struct command_descriptor {
74
+	/** Option descriptors */
75
+	struct option_descriptor *options;
76
+	/** Number of option descriptors */
77
+	uint8_t num_options;
78
+	/** Length of option structure */
79
+	uint8_t len;
80
+	/** Minimum number of non-option arguments */
81
+	uint8_t min_args;
82
+	/** Maximum number of non-option arguments */
83
+	uint8_t max_args;
84
+	/** Command usage and description
85
+	 *
86
+	 * This excludes the literal "Usage:" and the command name,
87
+	 * which will be prepended automatically.
88
+	 */
89
+	const char *usage_description;
90
+};
91
+
92
+/** No maximum number of arguments */
93
+#define MAX_ARGUMENTS 0xff
94
+
95
+/**
96
+ * Construct command descriptor
97
+ *
98
+ * @v _struct		Options structure type
99
+ * @v _options		Option descriptor array
100
+ * @v _check_args	Remaining argument checker
101
+ * @v _usage		Command usage
102
+ * @v _description	Command description
103
+ * @ret _command	Command descriptor
104
+ */
105
+#define COMMAND_DESC( _struct, _options, _min_args, _max_args, _usage,	      \
106
+		      _description )					      \
107
+	{								      \
108
+		.options = ( ( ( ( typeof ( _options[0] ) * ) NULL ) ==	      \
109
+			       ( ( struct option_descriptor * ) NULL ) ) ?    \
110
+			     _options : _options ),			      \
111
+		.num_options = ( sizeof ( _options ) /			      \
112
+				 sizeof ( _options[0] ) ),		      \
113
+		.len = sizeof ( _struct ),				      \
114
+		.min_args = _min_args,					      \
115
+		.max_args = _max_args,					      \
116
+		.usage_description = _usage "\n\n" _description,	      \
117
+	 }
118
+
119
+extern int parse_string ( const char *text, const char **value );
120
+extern int parse_integer ( const char *text, unsigned int *value );
121
+extern int parse_netdev ( const char *text, struct net_device **netdev );
122
+extern int parse_image ( const char *text, struct image **image );
123
+extern void print_usage ( struct command_descriptor *cmd, char **argv );
124
+extern int parse_options ( int argc, char **argv,
125
+			   struct command_descriptor *cmd, void *opts );
126
+
127
+#endif /* _IPXE_PARSEOPT_H */

Carregando…
Cancelar
Salvar