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