1 |
#include "GBase.h" |
2 |
#include "GArgs.h" |
3 |
#include <ctype.h> |
4 |
|
5 |
GArgs::GArgs(int argc, char* const argv[], const char* format, bool nodigitopts) { |
6 |
/* format is: |
7 |
<letter>[:] for e.g. p:hT <- -p testing -ptesting -h -T |
8 |
<string>= for e.g. PID=S= <- PID=50 S=3.5 |
9 |
This means that the = options, if present, must NEVER be given after |
10 |
dashed switches (non-value) directly |
11 |
*/ |
12 |
|
13 |
//parse format string first: |
14 |
const char* fstr=format; |
15 |
fmtcount=0; |
16 |
count=0; |
17 |
nonOptCount=0; |
18 |
nonOptPos=0; |
19 |
optPos=0; |
20 |
errarg=0; |
21 |
args=NULL; |
22 |
fmt=NULL; |
23 |
int fmtlen=strlen(format); |
24 |
while (fstr-format < fmtlen ) { |
25 |
int l=strcspn(fstr, ":="); |
26 |
if (fstr[l]=='\0') { //end of string reached |
27 |
//all previous chars are just switches: |
28 |
GREALLOC(fmt, (fmtcount+l)*sizeof(fmtdef)); |
29 |
//store each switches |
30 |
for (int i=0; i<l;i++) { |
31 |
GCALLOC(fmt[fmtcount+i].opt, 2); //one char length |
32 |
fmt[fmtcount+i].opt[0]=fstr[i]; |
33 |
fmt[fmtcount+i].type = 0; |
34 |
} |
35 |
fmtcount+=l; |
36 |
break; |
37 |
} |
38 |
else { |
39 |
if (fstr[l]==':') { |
40 |
//fstr[l-1] is an argument, but all the previous are just switches |
41 |
GREALLOC(fmt, (fmtcount+l)*sizeof(fmtdef)); |
42 |
//store each switches AND the option |
43 |
for (int i=0; i<l;i++) { |
44 |
GCALLOC(fmt[fmtcount+i].opt, 2); //one char length |
45 |
fmt[fmtcount+i].opt[0]=fstr[i]; |
46 |
fmt[fmtcount+i].type = (i==l-1)?1:0; |
47 |
} |
48 |
fmtcount+=l; |
49 |
} |
50 |
else { // fstr[l]=='=' case! |
51 |
//all these chars are one = style argument |
52 |
GREALLOC(fmt, (fmtcount+1)*sizeof(fmtdef)); |
53 |
GMALLOC(fmt[fmtcount].opt, l+1); |
54 |
strncpy(fmt[fmtcount].opt, fstr, l); |
55 |
fmt[fmtcount].opt[l]='\0'; |
56 |
fmt[fmtcount].type=2; |
57 |
fmtcount++; |
58 |
} |
59 |
fstr+=l+1; |
60 |
} |
61 |
} |
62 |
//---- that was the parsing of the format string |
63 |
//now parse the arguments based on given format specification |
64 |
int p=1; //skip program name |
65 |
int f=0; |
66 |
//GMessage("argc=%d\n", argc); |
67 |
while (p<argc) { |
68 |
if (argv[p][0]=='-') { //dashed argument ? |
69 |
int cpos=1; |
70 |
char c=argv[p][cpos]; |
71 |
if (c==0 || (nodigitopts && isdigit(c))) { |
72 |
//special case: plain argument '-' or negative number |
73 |
GREALLOC(args, (count+1)*sizeof(argdata)); |
74 |
args[count].opt=NULL; |
75 |
if (c==0) { |
76 |
GCALLOC(args[count].value, 2); |
77 |
args[count].value[0]='-'; |
78 |
} |
79 |
else { |
80 |
args[count].value=Gstrdup(argv[p]); |
81 |
} |
82 |
count++; |
83 |
nonOptCount++; |
84 |
} |
85 |
else { //dashed argument or switch |
86 |
COLLAPSED: |
87 |
if ((f=validOpt(c))>=0) { |
88 |
if (fmt[f].type==0) {//switch type |
89 |
GREALLOC(args, (count+1)*sizeof(argdata)); |
90 |
GCALLOC(args[count].opt, 2); |
91 |
args[count].opt[0]=c; |
92 |
GCALLOC(args[count].value, 1); |
93 |
count++; |
94 |
// only switches can be grouped with some other switches or options |
95 |
if (argv[p][cpos+1]!='\0') { |
96 |
cpos++; |
97 |
c=argv[p][cpos]; |
98 |
goto COLLAPSED; |
99 |
} |
100 |
} |
101 |
else |
102 |
if (fmt[f].type==1) { //dash argument |
103 |
GREALLOC(args, (count+1)*sizeof(argdata)); |
104 |
GCALLOC(args[count].opt, 2); |
105 |
args[count].opt[0]=c; |
106 |
if (argv[p][cpos+1]=='\0') { |
107 |
if (p+1<argc) { //value is the whole next argument |
108 |
p++; |
109 |
GMALLOC(args[count].value, strlen(argv[p])+1); |
110 |
strcpy(args[count].value, argv[p]); |
111 |
} |
112 |
else { |
113 |
errarg=p; |
114 |
return; |
115 |
} |
116 |
} |
117 |
else { //value immediately follows the dash-option |
118 |
GMALLOC(args[count].value, strlen(argv[p])-cpos); |
119 |
strcpy(args[count].value, (argv[p]+cpos+1)); |
120 |
//GMessage("args[%d].value = '%s'",count, args[count].value); |
121 |
} |
122 |
count++; |
123 |
} |
124 |
else {//inconsistent type |
125 |
errarg=p; |
126 |
return; |
127 |
} |
128 |
} //was validOpt |
129 |
else { //option not found in format definition! |
130 |
errarg=p; |
131 |
return; |
132 |
} |
133 |
} |
134 |
} |
135 |
else {//not a dashed argument |
136 |
char* e=strchr(argv[p],'='); |
137 |
if (e!=NULL && strchr(format,'=')!=NULL && e!=argv[p] && *(e-1)!='\\') { |
138 |
//this must be an '=' option |
139 |
//yet the '=' char can be preceded by a '\' in order to not be parsed |
140 |
//as a = option |
141 |
char part[128]; |
142 |
strncpy(part, argv[p], e-argv[p]); |
143 |
part[e-argv[p]]='\0'; |
144 |
if ((f=validOpt(part))>=0 && fmt[f].type==2) { |
145 |
GREALLOC(args, (count+1)*sizeof(argdata)); |
146 |
args[count].opt=Gstrdup(part); |
147 |
if (strlen(argv[p])-strlen(part)>0) { |
148 |
GMALLOC(args[count].value, strlen(argv[p])-strlen(part)+1); |
149 |
strcpy(args[count].value, e+1); |
150 |
} |
151 |
else { |
152 |
args[count].value=NULL; |
153 |
} |
154 |
count++; |
155 |
} |
156 |
else { //error - format does not match this '=' argument |
157 |
errarg=p; |
158 |
return; |
159 |
} |
160 |
} |
161 |
else { //it seems it's just a plain argument, like a filename, etc. |
162 |
GREALLOC(args, (count+1)*sizeof(argdata)); |
163 |
args[count].opt=NULL; //it's not an option |
164 |
args[count].value=Gstrdup(argv[p]); |
165 |
count++; |
166 |
nonOptCount++; |
167 |
} |
168 |
} |
169 |
p++;//check next arg string |
170 |
} |
171 |
} |
172 |
|
173 |
GArgs::~GArgs() { |
174 |
int i; |
175 |
for (i=0; i<fmtcount; i++) |
176 |
GFREE(fmt[i].opt); |
177 |
GFREE(fmt); |
178 |
for (i=0; i<count; i++) { |
179 |
GFREE(args[i].opt); |
180 |
GFREE(args[i].value); |
181 |
} |
182 |
GFREE(args); |
183 |
} |
184 |
|
185 |
int GArgs::validOpt(char o) { |
186 |
for (int i=0; i<fmtcount; i++) |
187 |
if (fmt[i].opt[0]==o && fmt[i].opt[1]=='\0') return i; |
188 |
return -1; |
189 |
} |
190 |
|
191 |
int GArgs::validOpt(char* o) { |
192 |
for (int i=0; i<fmtcount; i++) |
193 |
if (strcmp(fmt[i].opt, o)==0) return i; |
194 |
return -1; |
195 |
} |
196 |
|
197 |
int GArgs::isError() { // returns the offending argv position or 0 if no error |
198 |
return errarg; |
199 |
} |
200 |
|
201 |
char* GArgs::getOpt(const char* o) { /* retrieve the value for option o |
202 |
returns |
203 |
NULL if option not given at all |
204 |
!=NULL if boolean option was given |
205 |
opt.value if value option was given |
206 |
*/ |
207 |
for (int i=0; i<count; i++) |
208 |
if (args[i].opt!=NULL && strcmp(args[i].opt, o)==0) |
209 |
return args[i].value; |
210 |
return NULL; |
211 |
} |
212 |
|
213 |
char* GArgs::getOpt(const char o) { |
214 |
for (int i=0; i<count; i++) |
215 |
if (args[i].opt!=NULL && args[i].opt[0]==o && args[i].opt[1]=='\0') |
216 |
return args[i].value; |
217 |
return NULL; |
218 |
} |
219 |
|
220 |
int GArgs::startNonOpt(){ //reset iteration through non-dashed arguments |
221 |
//returns the number of non-dashed arguments |
222 |
nonOptPos=0; |
223 |
return nonOptCount; |
224 |
} |
225 |
|
226 |
|
227 |
char* GArgs::nextNonOpt() { //get the next non-dashed argument |
228 |
//or NULL if no more |
229 |
for (int i=nonOptPos;i<count;i++) |
230 |
if (args[i].opt==NULL) { |
231 |
nonOptPos=i+1; |
232 |
return args[i].value; |
233 |
} |
234 |
return NULL; |
235 |
} |
236 |
|
237 |
int GArgs::startOpt(){ //reset iteration through non-dashed arguments |
238 |
//returns the number of non-dashed arguments |
239 |
optPos=0; |
240 |
return count-nonOptCount; |
241 |
} |
242 |
|
243 |
|
244 |
char* GArgs::nextOpt() { //get the next non-dashed argument |
245 |
//or NULL if no more |
246 |
for (int i=optPos;i<count;i++) |
247 |
if (args[i].opt!=NULL) { |
248 |
optPos=i+1; |
249 |
return args[i].opt; |
250 |
} |
251 |
return NULL; |
252 |
} |