2 * This file Copyright (C) 2008-2014 Mnemosyne LLC
4 * It may be used under the GNU GPL versions 2 or 3
5 * or any future license endorsed by Mnemosyne LLC.
7 * $Id: tr-getopt.c 14241 2014-01-21 03:10:30Z jordan $
10 #include <ctype.h> /* isspace () */
12 #include <stdlib.h> /* exit () */
15 #include "tr-getopt.h"
18 #define MAX(a, b)(((a) > (b)) ? (a) : (b))
24 getArgName (const tr_option
* opt
)
30 else if (opt
->argName
)
39 get_next_line_len (const char * description
, int maxlen
)
42 int len
= strlen (description
);
47 end
= maxlen
< len
? maxlen
: len
;
48 while ((end
> 0) && !isspace (description
[end
]))
51 return end
? end
: len
;
55 getopts_usage_line (const tr_option
* opt
,
61 const char * longName
= opt
->longName
? opt
->longName
: "";
62 const char * shortName
= opt
->shortName
? opt
->shortName
: "";
63 const char * arg
= getArgName (opt
);
65 const int d_indent
= shortWidth
+ longWidth
+ argWidth
+ 7;
66 const int d_width
= 80 - d_indent
;
67 const char * d
= opt
->description
;
69 printf (" %s%-*s %s%-*s %-*s ",
70 (shortName
&& *shortName
? "-" : " "), shortWidth
, shortName
,
71 (longName
&& *longName
? "--" : " "), longWidth
, longName
,
73 len
= get_next_line_len (d
, d_width
);
74 printf ("%*.*s\n", len
, len
, d
);
77 while (isspace (*d
)) ++d
;
79 while ((len
= get_next_line_len (d
, d_width
))) {
80 printf ("%*.*s%*.*s\n", d_indent
, d_indent
, "", len
, len
, d
);
82 while (isspace (*d
)) ++d
;
87 maxWidth (const struct tr_option
* o
,
95 *longWidth
= MAX (*longWidth
, (int)strlen (o
->longName
));
98 *shortWidth
= MAX (*shortWidth
, (int)strlen (o
->shortName
));
100 if ((arg
= getArgName (o
)))
101 *argWidth
= MAX (*argWidth
, (int)strlen (arg
));
105 tr_getopt_usage (const char * progName
,
106 const char * description
,
107 const struct tr_option opts
[])
112 struct tr_option help
;
113 const struct tr_option
* o
;
115 for (o
= opts
; o
->val
; ++o
)
116 maxWidth (o
, &longWidth
, &shortWidth
, &argWidth
);
119 help
.longName
= "help";
120 help
.description
= "Display this help page and exit";
121 help
.shortName
= "h";
123 maxWidth (&help
, &longWidth
, &shortWidth
, &argWidth
);
125 if (description
== NULL
)
126 description
= "Usage: %s [options]";
127 printf (description
, progName
);
128 printf ("\n\nOptions:\n");
129 getopts_usage_line (&help
, longWidth
, shortWidth
, argWidth
);
130 for (o
= opts
; o
->val
; ++o
)
131 getopts_usage_line (o
, longWidth
, shortWidth
, argWidth
);
134 static const tr_option
*
135 findOption (const tr_option
* opts
,
137 const char ** setme_arg
)
140 const char * arg
= NULL
;
142 const tr_option
* match
= NULL
;
144 /* find the longest matching option */
145 for (o
= opts
; o
->val
; ++o
)
147 size_t len
= o
->longName
? strlen (o
->longName
) : 0;
152 && !strncmp (str
+2, o
->longName
, len
)
153 && (str
[len
+ 2] == '\0' || (o
->has_arg
&& str
[len
+ 2] == '=')))
157 arg
= str
[len
+ 2] == '=' ? str
+ len
+ 3 : NULL
;
160 len
= o
->shortName
? strlen (o
->shortName
) : 0;
164 && !strncmp (str
+1, o
->shortName
, len
)
165 && (str
[len
+ 1] == '\0' || o
->has_arg
))
169 switch (str
[len
+ 1])
175 arg
= str
+ len
+ 2; break;
178 arg
= str
+ len
+ 1; break;
190 tr_getopt (const char * usage
,
193 const tr_option
* opts
,
194 const char ** setme_optarg
)
197 const char * arg
= NULL
;
198 const tr_option
* o
= NULL
;
200 *setme_optarg
= NULL
;
202 /* handle the builtin 'help' option */
203 for (i
= 1; i
< argc
; ++i
)
205 if (!strcmp (argv
[i
], "-h") || !strcmp (argv
[i
], "--help"))
207 tr_getopt_usage (argv
[0], usage
, opts
);
212 /* out of options? */
213 if (argc
== 1 || tr_optind
>= argc
)
216 o
= findOption (opts
, argv
[tr_optind
], &arg
);
219 /* let the user know we got an unknown option... */
220 *setme_optarg
= argv
[tr_optind
++];
226 /* no argument needed for this option, so we're done */
229 *setme_optarg
= NULL
;
234 /* option needed an argument, and it was embedded in this string */
242 /* throw an error if the option needed an argument but didn't get one */
243 if (++tr_optind
>= argc
)
245 if (findOption (opts
, argv
[tr_optind
], NULL
))
248 *setme_optarg
= argv
[tr_optind
++];