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