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