-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathSUMA_StringParse.h
398 lines (366 loc) · 14.1 KB
/
SUMA_StringParse.h
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
#ifndef SUMA_STRING_PARSE_INCLUDED
#define SUMA_STRING_PARSE_INCLUDED
/* >>>>>>>>>>>>>>>>>>>>>>> Begin string parsing macros <<<<<<<<<<<<<<<<<<<<<<< */
/* See also SUMA_IS_DIGIT_CHAR, SUMA_IS_NUM_CHAR */
#define SUMA_IS_DIGIT(c) ( ((c) < '0' || (c) > '9') ? 0 : 1 )
#define SUMA_IS_PURE_BLANK(c) ( ((c) == ' ' || (c) == '\t' ) )
/*! a macro version of C's isspace
returns 1 if charater is considered a blank
\sa SUMA_SKIP_BLANK
*/
#define SUMA_IS_BLANK(c) ( (SUMA_IS_PURE_BLANK(c) || (c) == '\n' || (c) == '\v' || (c) == '\f' || (c) == '\r') ? 1 : 0 )
#define SUMA_IS_PUNCT(c) ( ((c) == '.' || (c) == ',' || (c) == ':' || (c) == ';') ? 1 : 0 )
/* See also SUMA_IS_EOL */
#define SUMA_IS_LINE_END(c) ( ((c) == '\n' || (c) == '\f' || (c) == '\r') ? 1 : 0 )
#define SUMA_IS_NICE_PREFIX_CHAR(c) ( (SUMA_IS_BLANK(c) \
|| (c) == ':' || (c) == ';' || (c) == '[' || (c) == ']' || (c) == '{' || (c) == '}' \
|| (c) == '<' || (c) == '>' || (c) == '#' || (c) == '*' || (c) == '?' ) \
? 1 : 0 )
/* Is this a sphinx underline character? */
#define SUMA_IS_UNDERLINE_CHAR(cc) ((\
(cc) == '-' || \
(cc) == '=' || \
(cc) == '*' || \
(cc) == '#' ) ? 1:0)
/*!
\brief advances pointer to next non-space, see isspace function for characters I check for.
op must be NULL terminated, if eop is NULL
eop is a limit address not to be reached by op
\sa SUMA_IS_BLANK
*/
#define SUMA_SKIP_BLANK(op, eop){ \
while (*op != '\0' && op != eop && SUMA_IS_BLANK(*op)) ++op; \
}
#define SUMA_SKIP_PURE_BLANK(op, eop){ \
while (*op != '\0' && op != eop && SUMA_IS_PURE_BLANK(*op)) ++op; \
}
#define SUMA_SKIP_LINE(op, eop){ \
while (*op != '\0' && op != eop && *op != '\n' && *op != '\f' && *op != '\r') ++op; \
SUMA_SKIP_BLANK(op, eop);\
}
#define SUMA_IS_COMMENT_LINE(opor, eop, cc, ans){ \
char *m_op = opor; \
ans = 0;\
SUMA_SKIP_BLANK(m_op, eop);\
if (*m_op == cc) { ans = 1; } \
}
#define SUMA_DEBLANK(name,repl) { \
if (name) { \
int m_i, m_r=0; \
if (repl != '\0') { \
for (m_i=0; m_i<strlen(name); ++m_i) { if (SUMA_IS_BLANK(name[m_i])) { name[m_i] = repl; } } \
} else { \
for (m_i=0; m_i<strlen(name); ++m_i) { if (!SUMA_IS_BLANK(name[m_i])) { name[m_r] = name[m_i]; ++m_r;} } \
name[m_r] = '\0'; \
} \
}\
}
#define SUMA_NICEATE_FILENAME(name,repl) { \
if (name) { \
int m_i, m_r=0; \
if (repl) { \
for (m_i=0; m_i<strlen(name); ++m_i) { if (SUMA_IS_NICE_PREFIX_CHAR(name[m_i])) { name[m_i] = repl; } } \
} else { \
for (m_i=0; m_i<strlen(name); ++m_i) { if (!SUMA_IS_NICE_PREFIX_CHAR(name[m_i])) { name[m_r] = name[m_i]; ++m_r;} } \
name[m_r] = '\0'; \
} \
}\
}
/*!
\brief advance pointer to next blank, skips quoted strings (works with " and ' combos, I hope)
Hello 'djjdk sskjd' Jon
if op[0] is the ' then after the macro, op[0] will be the space
just before Jon
op must be NULL terminated, if eop is NULL
eop is a limit address not to be reached by op
*/
#define SUMA_SKIP_TO_NEXT_BLANK(op, eop){ \
char m_quote_open = '\0'; \
while (*op != '\0' && op !=eop && !( !m_quote_open && (*op == ' ' || *op == '\t' || *op == '\n' || *op == '\v' || *op == '\f' || *op == '\r')) ) { \
if (*op == '"' || *op == '\'') { \
if (!m_quote_open) m_quote_open = *op; \
else if (m_quote_open == *op) m_quote_open = '\0'; \
} \
++op; \
} \
}
/* See also SUMA_IS_LINE_END */
#define SUMA_IS_EOL(cc) (( (cc) == '\n' || (cc) == '\v' \
||(cc) == '\f' || (cc) == '\r' || (cc) == '\0') )
#define SUMA_SKIP_TO_EOL(op, eop){ \
char m_quote_open = '\0'; \
while (*op != '\0' && op !=eop && !(!m_quote_open && SUMA_IS_EOL(*op)) ) { \
if (*op == '"' || *op == '\'') { \
if (!m_quote_open) m_quote_open = *op; \
else if (m_quote_open == *op) m_quote_open = '\0'; \
} \
++op; \
} \
}
#define SUMA_SKIP_TO_NEXT_CHAR(op, eop, ch){ \
char m_quote_open = '\0'; \
while (*op != '\0' && op !=eop && !( !m_quote_open && (*op == ch)) ) { \
if (*op == '"' || *op == '\'') { \
if (!m_quote_open) m_quote_open = *op; \
else if (m_quote_open == *op) m_quote_open = '\0'; \
} \
++op; \
} \
}
/*!
\brief Find the addresses limiting a section between two blanks,
Hello 'djjdk sskjd' Jon
if op is pointing the blank space somewhere after Hello then
op will then point to '
and op2 will point to the first blank after sskjd'
op must be NULL terminated, if eop is NULL
eop is a limit address not to be reached by op
*/
#define SUMA_GET_BETWEEN_BLANKS(op, eop, op2){ \
SUMA_SKIP_BLANK(op, eop); /* skip first blanks*/ \
op2 = op; /* skip till next blanks */ \
SUMA_SKIP_TO_NEXT_BLANK(op2, eop); \
}
#define SUMA_GET_TO_EOL(op, eop, op2){ \
if (!SUMA_IS_EOL(*op)) { \
SUMA_SKIP_BLANK(op, eop); /* skip first blanks*/ \
op2 = op; /* skip till next blanks */ \
SUMA_SKIP_TO_EOL(op2, eop); \
} else { op2 = op; } /* Stay right where you are */ \
}
/*! \brief Count number of blank delimited words.
Does not alter op or eop
Returns number of words in N_word;
*/
#define SUMA_COUNT_WORDS(op, eop, N_word){ \
char *m_ops=op, *m_opn=op; \
N_word = 0; \
do { \
m_ops = m_opn; SUMA_GET_BETWEEN_BLANKS(m_ops,eop,m_opn); \
if (m_opn > m_ops) ++N_word; \
} while (*m_opn != '\0' && m_opn != eop && m_ops != m_opn); \
}
/*!
\brief advance pointer past a string
\param op (char *) pointer to char array
\param eop (char *) DO not search op past eop or '\0' in op
DO NOT CALL THE MACRO WITH eop SET TO (op+Nchars), i.e. do not do this:
SUMA_ADVANCE_PAST(op,(op+5),attr,Found,Word); to check only 5 chars ahead
if you do so, a part of the if condition (op < eop) will always evaluate to
true because it is expanded to op < (op+5) !
\param attr (char *) character string searched (NULL terminated)
\Found (int) 0 --> Not found, op is not changed
1 --> Found, op is set just past the location of attr
\Word (int) 0 --> Search for an exact match of attr, regardless of how its surrounded
1 --> Make sure attr is surrounded by blanks
*/
#define SUMA_ADVANCE_PAST(op,eop,attr,Found,Word){ \
int m_natr = strlen(attr); \
char *m_bop = op; \
Found = 0; \
while (op < eop && *op != '\0' && Found < m_natr) { \
if (*op == attr[Found]) { \
/* found a match, increment match counter */ \
++Found; \
} else { /* no match, break */ \
Found = 0; \
} \
++op; \
if (Word && Found == m_natr) { /* make sure word is surrounded by blank */\
if ( !(*op == '\0' || op == eop || SUMA_IS_BLANK(*op)) ) { /* character after word is not blank */ \
Found = 0; /* reset */ \
} else { /* check for blank after word */ \
char *m_bef = op - m_natr - 1;/* pointer to character before attr */ \
if ( !(m_bef < m_bop || SUMA_IS_BLANK(*m_bef)) ) { /* character before word is not blank */ \
Found = 0; /* reset */ \
} \
} \
} \
} \
/* fprintf(SUMA_STDERR,"%s: Searched %d chars.\n", FuncName, op-m_bop); */\
if (Found != m_natr) { Found = 0; op = m_bop; }/* reset pointer to origin */ \
}
/*!
Like SUMA_ADVANCE_PAST, but uses a NULL-terminated list of words rather than just one word
char *key_list[] = { "static", "char", "FuncName", "=", "{" , "}", ";", NULL};
int *gap_list[] = { -1 , 5 , 5 , 5 , 2 , -1 , 20, -1 };
Find a sequence of keys where key_list[i] is present and follows key_list[i-1] by
less than gap_list[i] (if gap_list[i] is >= 0)
If the sequence is not found then:
nFound = 0 and op is unchanged and op_beg is NULL
else op is set past the sequence of strings, and op_beg is set to the beginning
of the string sequence
*/
#define SUMA_ADVANCE_PAST_SEQUENCE(op, eop, op_beg, key_list, gap, nFound, Word) { \
char *m_op_func , *m_op_prev; \
int m_good, m_found, m_d; \
nFound = 0; \
m_found = 0; \
m_good = 1; \
m_op_func = m_op_prev = op; \
while (key_list[nFound] && m_good) { \
/* if (LocalHead) fprintf(SUMA_STDERR,"key[%d]=%s ", nFound, key_list[nFound]); */\
m_op_prev = m_op_func; \
SUMA_ADVANCE_PAST(m_op_func, eop, key_list[nFound], m_found, Word); \
if (m_found > 0) { /* key found */\
/* if (LocalHead) fprintf(SUMA_STDERR," Found "); */\
if (!nFound || gap[nFound] < 0 || m_op_func - m_op_prev - strlen(key_list[nFound]) < gap[nFound]) { /* inside limit */ \
/* if (LocalHead) fprintf(SUMA_STDERR," in gap "); */\
if (!nFound) op_beg = m_op_func - strlen(key_list[0]); \
++nFound; \
} else { /* outside gap */ \
/* if (LocalHead) fprintf(SUMA_STDERR," out of gap (%d, Augment by %d) ", m_op_func - m_op_prev, (int)strlen(key_list[0])); */\
nFound = 0; /* reset search */ \
m_op_func = op_beg + strlen(key_list[0]); /* at after first key */ \
op_beg = m_op_func; \
} \
} else { \
{ /* nothing here */ \
m_good = 0; \
} \
} \
} \
if (!m_good) { \
/* SUMA_LH("Done."); */\
nFound = 0; \
op_beg = NULL; \
} else { \
op = m_op_func; \
} \
}
/*!
\brief advance pointer past the next number
Number has to start at op
\param op (char *) pointer to char array (NULL terminated)
\param num (double) output of strtod function
\Found (int) 0 --> Not found, op is not changed
1 --> Found, op is set just past the location after number
*/
#define SUMA_ADVANCE_PAST_NUM(op, num, Found){\
char *m_ope=NULL; \
Found = 0; \
num = strtod(op, &m_ope); \
if (m_ope > op) { /* something found */ \
Found = 1; \
op = m_ope; \
} else { /* just to be safe */\
num = 0;\
} \
}
/*!
\brief advance pointer past the next integer.
Unlike SUMA_ADVANCE_PAST_NUM,
integer does not have to start at op
\param op (char *) pointer to char array (NULL terminated)
\param num (int) output of (int) strtod function
\Found (int) 0 --> Not found, op is not changed
1 --> Found, op is set just past the location after number
*/
#define SUMA_ADVANCE_PAST_INT(op, num, Found){\
char *m_ope=NULL; \
Found = 0; \
m_ope = op; \
while (!SUMA_IS_DIGIT(*m_ope)) ++m_ope; /* skip till digit */\
num = (int)strtod(m_ope, &m_ope); \
if (m_ope > op) { /* something found */ \
Found = 1; \
op = m_ope; \
} else { /* just to be safe */\
num = 0;\
} \
}
/*!
\brief copies characters between [op,op2[ into a new NULL terminated string str
str should be freed with SUMA_free
*/
#define SUMA_COPY_TO_STRING(op,op2,sval){ \
int m_imax, m_i; \
if (!op) { SUMA_SL_Err("NULL input"); }\
if (sval) { SUMA_SL_Err("sval must be null when macro is called"); } \
else {\
if (op2 == NULL) op2 = op+strlen(op); \
if (op2 > op) { /* copy the deed */ \
m_imax = op2 - op; \
if (m_imax > 5000) { SUMA_SL_Warn("Unexpectedly large field!"); } \
sval = (char *)SUMA_calloc(m_imax + 2, sizeof(char)); \
if (!sval) { SUMA_S_Crit("Failed To Allocate"); } \
else { \
for (m_i=0; m_i < m_imax; ++m_i) { sval[m_i] = op[m_i]; } \
sval[m_imax] = '\0'; \
}\
} \
} \
}
/*!
\brief Fills characters between [m_op,m_op2[ into a preallocated string str
*/
#define SUMA_FILL_STRING(m_op,m_op2,strinp){ \
char *sval = strinp;\
if (!m_op2) { /* cm_opy till end */ \
while (*m_op != '\0') { *sval = *m_op; ++sval; ++m_op; } \
} else { \
while (*m_op != '\0' && m_op < (char*)m_op2) { \
*sval = *m_op; ++sval; ++m_op; \
} \
} \
*sval = '\0'; \
}
/*!
\brief Fills N characters between [m_op,m_op2[ into a preallocated string str
Make sure str can take N+1 chars
*/
#define SUMA_NFILL_STRING(m_op,m_op2,strinp,N){ \
char *sval = strinp; int m_n=0;\
if (!m_op2) { /* cm_opy till end */ \
while (*m_op != '\0' && m_n < N) { *sval = *m_op; ++sval; ++m_op; ++m_n;} \
} else { \
while (*m_op != '\0' && m_op < (char*)m_op2 && m_n < N) { \
*sval = *m_op; ++sval; ++m_op; ++m_n;\
} \
} \
*sval = '\0'; \
}
/*!
\brief print N characters between [op,op2[ into file pointer out
Leaves op and op2 in place
*/
#define SUMA_NPRINT_STRING(opi,op2,out,N, head, foot){ \
int m_n=0; FILE *m_outp=SUMA_STDERR; char *m_op=opi, *m_op2=op2;\
if (out) m_outp=out; \
if (head) fprintf(m_outp,"%s", head); \
if (!m_op2) { /* write till end */ \
while (*m_op != '\0' && m_n < N) { \
fprintf(m_outp,"%c", *m_op); ++m_op; ++m_n;\
} \
} else { \
while (*m_op != '\0' && m_op < (char*)m_op2 && m_n < N) { \
fprintf(m_outp,"%c", *m_op); ++m_op; ++m_n;\
} \
} \
if (foot) fprintf(m_outp,"%s", foot); \
}
/*!
\brief print characters between [op,op2[ into file pointer out
Leaves op and op2 in place
*/
#define SUMA_PRINT_STRING(opi,opi2,out,head, foot){ \
FILE *m_outp=SUMA_STDERR; char *m_op=opi, *m_op2 = opi2;\
if (out) m_outp=out; \
if (head) fprintf(m_outp,"%s", head); \
if (!m_op2) { /* write till end */ \
while (*m_op != '\0') { fprintf(m_outp,"%c", *m_op); ++m_op;} \
} else { \
while (*m_op != '\0' && m_op < (char*)m_op2) { \
fprintf(m_outp,"%c", *m_op); ++m_op;\
} \
} \
if (foot) fprintf(m_outp,"%s", foot); \
}
#define SUMA_DEBLANK_RHS(s) {\
int ns; \
if (s && (ns=strlen(s))) { --ns; \
while (ns >= 0 && SUMA_IS_BLANK(s[ns])) { s[ns]='\0'; --ns; } \
}\
}\
/* >>>>>>>>>>>>>>>>>>>>>>> End string parsing macros <<<<<<<<<<<<<<<<<<<<<<<< */
#endif