A modern Music Player Daemon based on Rockbox open source high quality audio player
libadwaita
audio
rust
zig
deno
mpris
rockbox
mpd
1/***************************************************************************
2 * __________ __ ___.
3 * Open \______ \ ____ ____ | | _\_ |__ _______ ___
4 * Source | _// _ \_/ ___\| |/ /| __ \ / _ \ \/ /
5 * Jukebox | | ( <_> ) \___| < | \_\ ( <_> > < <
6 * Firmware |____|_ /\____/ \___ >__|_ \|___ /\____/__/\_ \
7 * \/ \/ \/ \/ \/
8 * $Id$
9 *
10 * Copyright (C) 2021 William Wilgus
11 *
12 * This program is free software; you can redistribute it and/or
13 * modify it under the terms of the GNU General Public License
14 * as published by the Free Software Foundation; either version 2
15 * of the License, or (at your option) any later version.
16 *
17 * This software is distributed on an "AS IS" basis, WITHOUT WARRANTY OF ANY
18 * KIND, either express or implied.
19 *
20 ****************************************************************************/
21
22#include "plugin.h"
23#include "arg_helper.h"
24
25#ifndef logf
26#define logf(...) {}
27#endif
28
29#define SWCHAR '-'
30#define DECSEPCHAR '.'
31#ifdef PLUGIN
32 #define strchr rb->strchr
33#endif
34int string_parse(const char **parameter, char *buf, size_t buf_sz)
35{
36/* fills buf with a string upto buf_sz, null terminates the buffer
37 * strings break on WS by default but can be enclosed in single or double quotes
38 * opening and closing quotes will not be included in the buffer but will be counted
39 * use alternating quotes if you really want them included '"text"' or "'text'"
40 * failure to close the string will result in eating all remaining args till \0
41 * If buffer full remaining chars are discarded till stopchar or \0 is reached */
42
43 char stopchar = ' ';
44 char stopchars[] = "\'\"";
45 int skipped = 0;
46 int found = 0;
47 if (!parameter || !*parameter)
48 {
49 *buf = '\0';
50 return 0;
51 }
52 const char* start = *parameter;
53
54 if (strchr(stopchars, *start))
55 {
56 logf("stop char %c\n", *start);
57 stopchar = *start;
58 skipped++;
59 start++;
60 }
61 while (*start && *start != stopchar)
62 {
63 if (buf_sz > 1)
64 {
65 *buf++ = *start;
66 buf_sz--;
67 }
68 found++;
69 start++;
70 }
71 if (*start == stopchar && skipped)
72 {
73 start++;
74 skipped++;
75 }
76
77 *buf = '\0';
78
79 if (found > 0)
80 *parameter = start;
81 else
82 skipped = 0;
83
84 return found + skipped;
85}
86
87int char_parse(const char **parameter, char *character)
88{
89/* passes *character a single character eats remaining non-WS characters */
90 char buf[2];
91 int ret = string_parse(parameter, buf, sizeof(buf));
92 if (ret && character)
93 *character = buf[0];
94 return ret;
95
96}
97
98int bool_parse(const char **parameter, bool *choice)
99{
100/* determine true false using the first character the rest are skipped/ignored */
101 int found = 0;
102 const char tf_val[]="fn0ty1";/* false chars on left f/t should be balanced fffttt */
103 if (!parameter || !*parameter)
104 return 0;
105 const char* start = *parameter;
106
107
108 char c = tolower(*start);
109 const char *tfval = strchr(tf_val, c);
110 while(isalnum(*++start)) {;}
111
112 if (tfval)
113 {
114 found = start - (*parameter);
115 *parameter = start;
116 }
117
118 if (choice)
119 *choice = (tfval - tf_val) > (signed int) (sizeof(tf_val) / 2) - 1;
120
121 return found;
122}
123
124int longnum_parse(const char **parameter, long *number, long *decimal)
125{
126/* passes number and or decimal portion of number base 10 only..
127 fractional portion is scaled by ARGPARSE_FRAC_DEC_MULTIPLIER
128 Example (if ARGPARSE_FRAC_DEC_MULTIPLIER = 10 000)
129 meaning .0009 returns 9 , 9 / 10000 = .0009
130 .009 returns 90
131 .099 returns 990
132 .09 returns 900
133 .9 returns 9000
134 .9999 returns 9999
135*/
136
137 long num = 0;
138 long dec = 0;
139 int found = 0;
140 int neg = 0;
141 int digits = 0;
142 //logf ("n: %s\n", *parameter);
143 if (!parameter || !*parameter)
144 return 0;
145 const char* start = *parameter;
146
147 if (*start == '-')
148 {
149 neg = 1;
150 start++;
151 }
152 while (isdigit(*start))
153 {
154 found++;
155 num = num *10 + *start - '0';
156 start++;
157 }
158
159 if (*start == DECSEPCHAR)
160 {
161 start++;
162 while(*start == '0')
163 {
164 digits++;
165 start++;
166 }
167 while (isdigit(*start))
168 {
169 dec = dec *10 + *start - '0';
170 digits++;
171 start++;
172 }
173 if (decimal && digits <= ARGPARSE_MAX_FRAC_DIGITS)
174 {
175 if(digits < ARGPARSE_MAX_FRAC_DIGITS)
176 {
177 digits = ARGPARSE_MAX_FRAC_DIGITS - digits;
178 while (digits--)
179 dec *= 10;
180 }
181 }
182 else
183 dec = -1; /* error */
184 }
185
186 if (found > 0)
187 {
188 found = start - (*parameter);
189 *parameter = start;
190 }
191
192 if(number)
193 *number = neg ? -num : num;
194
195 if (decimal)
196 *decimal = dec;
197
198 return found;
199}
200
201int num_parse(const char **parameter, int *number, int *decimal)
202{
203 long num, dec;
204 int ret = longnum_parse(parameter, &num, &dec);
205 if(number)
206 *number = num;
207 if (decimal)
208 *decimal = dec;
209 return ret;
210}
211
212/*
213*argparse(const char *parameter, int parameter_len,
214* int (*arg_callback)(char argchar, const char **parameter))
215* parameter : constant char string of arguments
216* parameter_len : may be set to -1 if your parameter string is NULL (\0) terminated
217* arg_callback : function gets called for each SWCHAR found in the parameter string
218* Note: WS at beginning is stripped, **parameter starts at the first NON WS char
219* return 0 for arg_callback to quit parsing immediately
220*/
221void argparse(const char *parameter, int parameter_len, void *userdata,
222 int (*arg_callback)(char argchar, const char **parameter, void *userdata))
223{
224 bool lastchr;
225 char argchar;
226 const char *start = parameter;
227 while (parameter_len < 0 || (parameter - start) < parameter_len)
228 {
229 switch (*parameter++)
230 {
231 case SWCHAR:
232 {
233 if ((*parameter) == '\0')
234 return;
235
236 if (parameter_len < 0) { logf ("%s\n", parameter); }
237 else { logf ("%.*s\n", plen, parameter); }
238
239 argchar = *parameter;
240 lastchr = (*(parameter + 1) == '\0');
241 while (*++parameter || lastchr)
242 {
243 lastchr = false;
244 if (isspace(*parameter))
245 continue; /* eat spaces at beginning */
246 if (!arg_callback(argchar, ¶meter, userdata))
247 return;
248 break;
249 }
250 break;
251 }
252 case '\0':
253 {
254 if (parameter_len <= 0)
255 return;
256 }
257 }
258 }
259}
260
261/* EXAMPLE USAGE
262argparse("-n 42 -N 9.9 -n -78.9009 -f -P /rockbox/path/f -s 'Yestest' -B false -B 0 -B true -b n -by -b 1-c ops -c s -k", -1, &arg_callback);
263
264int arg_callback(char argchar, const char **parameter)
265{
266 int ret;
267 int num, dec;
268 char c;
269 char buf[32];
270 bool bret;
271 logf ("Arg: %c\n", argchar);
272 switch (tolower(argchar))
273 {
274 case 'k' :
275 logf("Option K!");
276 break;
277 case 'c' :
278 ret = char_parse(parameter, &c);
279 if (ret)
280 {
281 logf ("Val: %c\n", c);
282 logf("ate %d chars\n", ret);
283 }
284 break;
285
286 case 'n' :
287 ret = num_parse(parameter, &num, &dec);
288 if (ret)
289 {
290 logf ("Val: %d.%d\n", num, dec);
291 logf("ate %d chars\n", ret);
292 }
293 break;
294 case 's' :
295 ret = string_parse(parameter, buf, sizeof(buf));
296 if (ret)
297 {
298 logf ("Val: %s\n", buf);
299 logf("ate %d chars\n", ret);
300 }
301 break;
302 case 'p' :
303 ret = string_parse(parameter, buf, sizeof(buf));
304 if (ret)
305 {
306 logf ("Path: %s\n", buf);
307 logf("ate %d chars\n", ret);
308 }
309 break;
310 case 'b' :
311 ret = bool_parse(parameter, &bret);
312 if (ret)
313 {
314 logf ("Val: %s\n", bret ? "true" : "false");
315 logf("ate %d chars\n", ret);
316 }
317 break;
318 default :
319 logf ("Unknown switch '%c'\n",argchar);
320 //return 0;
321 }
322 return 1;
323}
324*/
325