1 /* put parameters in linked list and retrieve */
2
3 #include <ctype.h>
4 #include <stddef.h>
5 #include <stdio.h>
6 #include <stdlib.h>
7 #include <string.h>
8
9 #include "proj.h"
10 #include "proj_internal.h"
11
unquote_string(char * param_str)12 static void unquote_string(char* param_str) {
13
14 size_t len = strlen(param_str);
15 // Remove leading and terminating spaces after equal sign
16 const char* equal = strstr(param_str, "=\"");
17 if( equal && equal - param_str + 1 > 2 && param_str[len-1] == '"' ) {
18 size_t dst = equal + 1 - param_str;
19 size_t src = dst + 1;
20 for( ; param_str[src]; dst++, src++)
21 {
22 if( param_str[src] == '"' ) {
23 if( param_str[src+1] == '"' ) {
24 src++;
25 } else {
26 break;
27 }
28 }
29 param_str[dst] = param_str[src];
30 }
31 param_str[dst] = '\0';
32 }
33
34 }
35
36
37 /* create parameter list entry */
pj_mkparam(const char * str)38 paralist *pj_mkparam(const char *str) {
39 paralist *newitem;
40
41 if((newitem = (paralist *)pj_malloc(sizeof(paralist) + strlen(str))) != nullptr) {
42 newitem->used = 0;
43 newitem->next = nullptr;
44 if (*str == '+')
45 ++str;
46 (void)strcpy(newitem->param, str);
47 unquote_string(newitem->param);
48 }
49 return newitem;
50 }
51
52
53 /* As pj_mkparam, but payload ends at first whitespace, rather than at end of <str> */
pj_mkparam_ws(const char * str,const char ** next_str)54 paralist *pj_mkparam_ws (const char *str, const char **next_str) {
55 paralist *newitem;
56 size_t len = 0;
57
58 if (nullptr==str)
59 return nullptr;
60
61 /* Find start and length of string */
62 while (isspace (*str))
63 str++;
64 if (*str == '+')
65 str++;
66 bool in_string = false;
67 for( ; str[len] != '\0'; len++ ) {
68 if( in_string ) {
69 if( str[len] == '"' && str[len+1] == '"' ) {
70 len++;
71 } else if( str[len] == '"' ) {
72 in_string = false;
73 }
74 } else if( str[len] == '=' && str[len+1] == '"' ) {
75 in_string = true;
76 } else if( isspace(str[len]) ) {
77 break;
78 }
79 }
80
81 if( next_str )
82 *next_str = str + len;
83
84 /* Use calloc to automagically 0-terminate the copy */
85 newitem = (paralist *) pj_calloc (1, sizeof(paralist) + len + 1);
86 if (nullptr==newitem)
87 return nullptr;
88 memcpy(newitem->param, str, len);
89 unquote_string(newitem->param);
90
91 newitem->used = 0;
92 newitem->next = nullptr;
93
94 return newitem;
95 }
96
97 /**************************************************************************************/
pj_param_exists(paralist * list,const char * parameter)98 paralist *pj_param_exists (paralist *list, const char *parameter) {
99 /***************************************************************************************
100 Determine whether a given parameter exists in a paralist. If it does, return
101 a pointer to the corresponding list element - otherwise return 0.
102
103 In support of the pipeline syntax, the search is terminated once a "+step" list
104 element is reached, in which case a 0 is returned, unless the parameter
105 searched for is actually "step", in which case a pointer to the "step" list
106 element is returned.
107
108 This function is equivalent to the pj_param (...) call with the "opt" argument
109 set to the parameter name preceeeded by a 't'. But by using this one, one avoids
110 writing the code allocating memory for a new copy of parameter name, and prepending
111 the t (for compile time known names, this is obviously not an issue).
112 ***************************************************************************************/
113 paralist *next = list;
114 const char *c = strchr (parameter, '=');
115 size_t len = strlen (parameter);
116 if (c)
117 len = c - parameter;
118 if (list==nullptr)
119 return nullptr;
120
121 for (next = list; next; next = next->next) {
122 if (0==strncmp (parameter, next->param, len) && (next->param[len]=='=' || next->param[len]==0)) {
123 next->used = 1;
124 return next;
125 }
126 if (0==strcmp (parameter, "step"))
127 return nullptr;
128 }
129
130 return nullptr;
131 }
132
133
134 /************************************************************************/
135 /* pj_param() */
136 /* */
137 /* Test for presence or get parameter value. The first */
138 /* character in `opt' is a parameter type which can take the */
139 /* values: */
140 /* */
141 /* `t' - test for presence, return TRUE/FALSE in PROJVALUE.i */
142 /* `i' - integer value returned in PROJVALUE.i */
143 /* `d' - simple valued real input returned in PROJVALUE.f */
144 /* `r' - degrees (DMS translation applied), returned as */
145 /* radians in PROJVALUE.f */
146 /* `s' - string returned in PROJVALUE.s */
147 /* `b' - test for t/T/f/F, return in PROJVALUE.i */
148 /* */
149 /* Search is terminated when "step" is found, in which case */
150 /* 0 is returned, unless "step" was the target searched for. */
151 /* */
152 /************************************************************************/
153
pj_param(projCtx ctx,paralist * pl,const char * opt)154 PROJVALUE pj_param (projCtx ctx, paralist *pl, const char *opt) {
155
156 int type;
157 unsigned l;
158 PROJVALUE value = {0};
159
160 if ( ctx == nullptr )
161 ctx = pj_get_default_ctx();
162
163 type = *opt++;
164
165 if (nullptr==strchr ("tbirds", type)) {
166 fprintf(stderr, "invalid request to pj_param, fatal\n");
167 exit(1);
168 }
169
170 pl = pj_param_exists (pl, opt);
171 if (type == 't') {
172 value.i = pl != nullptr;
173 return value;
174 }
175
176 /* Not found */
177 if (nullptr==pl) {
178 /* Return value after the switch, so that the return path is */
179 /* taken in all cases */
180 switch (type) {
181 case 'b': case 'i':
182 value.i = 0;
183 break;
184 case 'd': case 'r':
185 value.f = 0.;
186 break;
187 case 's':
188 value.s = nullptr;
189 break;
190 }
191 return value;
192 }
193
194 /* Found parameter - now find its value */
195 pl->used |= 1;
196 l = (int) strlen(opt);
197 opt = pl->param + l;
198 if (*opt == '=')
199 ++opt;
200
201 switch (type) {
202 case 'i': /* integer input */
203 value.i = atoi(opt);
204 break;
205 case 'd': /* simple real input */
206 value.f = pj_atof(opt);
207 break;
208 case 'r': /* degrees input */
209 value.f = dmstor_ctx(ctx, opt, nullptr);
210 break;
211 case 's': /* char string */
212 value.s = (char *) opt;
213 break;
214 case 'b': /* boolean */
215 switch (*opt) {
216 case 'F': case 'f':
217 value.i = 0;
218 break;
219 case '\0': case 'T': case 't':
220 value.i = 1;
221 break;
222 default:
223 pj_ctx_set_errno (ctx, PJD_ERR_INVALID_BOOLEAN_PARAM);
224 value.i = 0;
225 break;
226 }
227 break;
228 }
229 return value;
230 }
231