#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 character 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