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