Imported Upstream version 1.6
[platform/upstream/ed.git] / carg_parser.c
1 /*  Arg_parser - POSIX/GNU command line argument parser. (C version)
2     Copyright (C) 2006, 2007, 2008, 2009, 2010, 2011, 2012
3     Antonio Diaz Diaz.
4
5     This library is free software: you can redistribute it and/or modify
6     it under the terms of the GNU General Public License as published by
7     the Free Software Foundation, either version 3 of the License, or
8     (at your option) any later version.
9
10     This library is distributed in the hope that it will be useful,
11     but WITHOUT ANY WARRANTY; without even the implied warranty of
12     MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13     GNU General Public License for more details.
14
15     You should have received a copy of the GNU General Public License
16     along with this library.  If not, see <http://www.gnu.org/licenses/>.
17
18     As a special exception, you may use this file as part of a free
19     software library without restriction.  Specifically, if other files
20     instantiate templates or use macros or inline functions from this
21     file, or you compile this file and link it with other files to
22     produce an executable, this file does not by itself cause the
23     resulting executable to be covered by the GNU General Public
24     License.  This exception does not however invalidate any other
25     reasons why the executable file might be covered by the GNU General
26     Public License.
27 */
28
29 #include <stdlib.h>
30 #include <string.h>
31
32 #include "carg_parser.h"
33
34
35 /* assure at least a minimum size for buffer `buf' */
36 static void * ap_resize_buffer( void * buf, const int min_size )
37   {
38   if( buf ) buf = realloc( buf, min_size );
39   else buf = malloc( min_size );
40   return buf;
41   }
42
43
44 static char push_back_record( struct Arg_parser * const ap,
45                               const int code, const char * const argument )
46   {
47   const int len = strlen( argument );
48   struct ap_Record *p;
49   void * tmp = ap_resize_buffer( ap->data,
50                  ( ap->data_size + 1 ) * sizeof (struct ap_Record) );
51   if( !tmp ) return 0;
52   ap->data = (struct ap_Record *)tmp;
53   p = &(ap->data[ap->data_size]);
54   p->code = code;
55   p->argument = 0;
56   tmp = ap_resize_buffer( p->argument, len + 1 );
57   if( !tmp ) return 0;
58   p->argument = (char *)tmp;
59   strncpy( p->argument, argument, len + 1 );
60   ++ap->data_size;
61   return 1;
62   }
63
64
65 static char add_error( struct Arg_parser * const ap, const char * const msg )
66   {
67   const int len = strlen( msg );
68   void * tmp = ap_resize_buffer( ap->error, ap->error_size + len + 1 );
69   if( !tmp ) return 0;
70   ap->error = (char *)tmp;
71   strncpy( ap->error + ap->error_size, msg, len + 1 );
72   ap->error_size += len;
73   return 1;
74   }
75
76
77 static void free_data( struct Arg_parser * const ap )
78   {
79   int i;
80   for( i = 0; i < ap->data_size; ++i ) free( ap->data[i].argument );
81   if( ap->data ) { free( ap->data ); ap->data = 0; }
82   ap->data_size = 0;
83   }
84
85
86 static char parse_long_option( struct Arg_parser * const ap,
87                                const char * const opt, const char * const arg,
88                                const struct ap_Option options[],
89                                int * const argindp )
90   {
91   unsigned int len;
92   int index = -1;
93   int i;
94   char exact = 0, ambig = 0;
95
96   for( len = 0; opt[len+2] && opt[len+2] != '='; ++len ) ;
97
98   /* Test all long options for either exact match or abbreviated matches. */
99   for( i = 0; options[i].code != 0; ++i )
100     if( options[i].name && !strncmp( options[i].name, &opt[2], len ) )
101       {
102       if( strlen( options[i].name ) == len )    /* Exact match found */
103         { index = i; exact = 1; break; }
104       else if( index < 0 ) index = i;           /* First nonexact match found */
105       else if( options[index].code != options[i].code ||
106                options[index].has_arg != options[i].has_arg )
107         ambig = 1;                      /* Second or later nonexact match found */
108       }
109
110   if( ambig && !exact )
111     {
112     add_error( ap, "option `" ); add_error( ap, opt );
113     add_error( ap, "' is ambiguous" );
114     return 1;
115     }
116
117   if( index < 0 )               /* nothing found */
118     {
119     add_error( ap, "unrecognized option `" ); add_error( ap, opt );
120     add_error( ap, "'" );
121     return 1;
122     }
123
124   ++*argindp;
125
126   if( opt[len+2] )              /* `--<long_option>=<argument>' syntax */
127     {
128     if( options[index].has_arg == ap_no )
129       {
130       add_error( ap, "option `--" ); add_error( ap, options[index].name );
131       add_error( ap, "' doesn't allow an argument" );
132       return 1;
133       }
134     if( options[index].has_arg == ap_yes && !opt[len+3] )
135       {
136       add_error( ap, "option `--" ); add_error( ap, options[index].name );
137       add_error( ap, "' requires an argument" );
138       return 1;
139       }
140     return push_back_record( ap, options[index].code, &opt[len+3] );
141     }
142
143   if( options[index].has_arg == ap_yes )
144     {
145     if( !arg || !arg[0] )
146       {
147       add_error( ap, "option `--" ); add_error( ap, options[index].name );
148       add_error( ap, "' requires an argument" );
149       return 1;
150       }
151     ++*argindp;
152     return push_back_record( ap, options[index].code, arg );
153     }
154
155   return push_back_record( ap, options[index].code, "" );
156   }
157
158
159 static char parse_short_option( struct Arg_parser * const ap,
160                                 const char * const opt, const char * const arg,
161                                 const struct ap_Option options[],
162                                 int * const argindp )
163   {
164   int cind = 1;                 /* character index in opt */
165
166   while( cind > 0 )
167     {
168     int index = -1;
169     int i;
170     const unsigned char code = opt[cind];
171     char code_str[2];
172     code_str[0] = code; code_str[1] = 0;
173
174     if( code != 0 )
175       for( i = 0; options[i].code; ++i )
176         if( code == options[i].code )
177           { index = i; break; }
178
179     if( index < 0 )
180       {
181       add_error( ap, "invalid option -- " ); add_error( ap, code_str );
182       return 1;
183       }
184
185     if( opt[++cind] == 0 ) { ++*argindp; cind = 0; }    /* opt finished */
186
187     if( options[index].has_arg != ap_no && cind > 0 && opt[cind] )
188       {
189       if( !push_back_record( ap, code, &opt[cind] ) ) return 0;
190       ++*argindp; cind = 0;
191       }
192     else if( options[index].has_arg == ap_yes )
193       {
194       if( !arg || !arg[0] )
195         {
196         add_error( ap, "option requires an argument -- " );
197         add_error( ap, code_str );
198         return 1;
199         }
200       ++*argindp; cind = 0;
201       if( !push_back_record( ap, code, arg ) ) return 0;
202       }
203     else if( !push_back_record( ap, code, "" ) ) return 0;
204     }
205   return 1;
206   }
207
208
209 char ap_init( struct Arg_parser * const ap,
210               const int argc, const char * const argv[],
211               const struct ap_Option options[], const char in_order )
212   {
213   const char ** non_options = 0;        /* skipped non-options */
214   int non_options_size = 0;             /* number of skipped non-options */
215   int argind = 1;                       /* index in argv */
216   int i;
217
218   ap->data = 0;
219   ap->error = 0;
220   ap->data_size = 0;
221   ap->error_size = 0;
222   if( argc < 2 || !argv || !options ) return 1;
223
224   while( argind < argc )
225     {
226     const unsigned char ch1 = argv[argind][0];
227     const unsigned char ch2 = ( ch1 ? argv[argind][1] : 0 );
228
229     if( ch1 == '-' && ch2 )             /* we found an option */
230       {
231       const char * const opt = argv[argind];
232       const char * const arg = (argind + 1 < argc) ? argv[argind+1] : 0;
233       if( ch2 == '-' )
234         {
235         if( !argv[argind][2] ) { ++argind; break; }     /* we found "--" */
236         else if( !parse_long_option( ap, opt, arg, options, &argind ) ) return 0;
237         }
238       else if( !parse_short_option( ap, opt, arg, options, &argind ) ) return 0;
239       if( ap->error ) break;
240       }
241     else
242       {
243       if( !in_order )
244         {
245         void * tmp = ap_resize_buffer( non_options,
246                        ( non_options_size + 1 ) * sizeof *non_options );
247         if( !tmp ) return 0;
248         non_options = (const char **)tmp;
249         non_options[non_options_size++] = argv[argind++];
250         }
251       else if( !push_back_record( ap, 0, argv[argind++] ) ) return 0;
252       }
253     }
254   if( ap->error ) free_data( ap );
255   else
256     {
257     for( i = 0; i < non_options_size; ++i )
258       if( !push_back_record( ap, 0, non_options[i] ) ) return 0;
259     while( argind < argc )
260       if( !push_back_record( ap, 0, argv[argind++] ) ) return 0;
261     }
262   if( non_options ) free( non_options );
263   return 1;
264   }
265
266
267 void ap_free( struct Arg_parser * const ap )
268   {
269   free_data( ap );
270   if( ap->error ) { free( ap->error ); ap->error = 0; }
271   ap->error_size = 0;
272   }
273
274
275 const char * ap_error( const struct Arg_parser * const ap )
276   { return ap->error; }
277
278
279 int ap_arguments( const struct Arg_parser * const ap )
280   { return ap->data_size; }
281
282
283 int ap_code( const struct Arg_parser * const ap, const int i )
284   {
285   if( i >= 0 && i < ap_arguments( ap ) ) return ap->data[i].code;
286   else return 0;
287   }
288
289
290 const char * ap_argument( const struct Arg_parser * const ap, const int i )
291   {
292   if( i >= 0 && i < ap_arguments( ap ) ) return ap->data[i].argument;
293   else return "";
294   }