X-Git-Url: https://sigrok.org/gitweb/?a=blobdiff_plain;f=src%2Fstrutil.c;h=9376ff09ce24682e2677fa2c54165ab2ad74406d;hb=HEAD;hp=c389a524d26598aa20a4274969cbd1bf9c76ae4b;hpb=a1d7443ba4130ffedfe657b59a77c00bc616662a;p=libsigrok.git diff --git a/src/strutil.c b/src/strutil.c index c389a524..2136bd49 100644 --- a/src/strutil.c +++ b/src/strutil.c @@ -174,7 +174,7 @@ SR_PRIV int sr_atoul_base(const char *str, unsigned long *ret, char **end, int b /* Add "0b" prefix support which strtol(3) may be missing. */ while (str && isspace(*str)) str++; - if (!base && strncmp(str, "0b", strlen("0b")) == 0) { + if ((!base || base == 2) && strncmp(str, "0b", strlen("0b")) == 0) { str += strlen("0b"); base = 2; } @@ -1667,4 +1667,209 @@ SR_API void sr_free_probe_names(char **names) g_strfreev(names); } +/** + * Trim leading and trailing whitespace off text. + * + * @param[in] s The input text. + * + * @return Start of trimmed input text. + * + * Manipulates the caller's input text in place. + * + * @since 0.6.0 + */ +SR_API char *sr_text_trim_spaces(char *s) +{ + char *p; + + if (!s || !*s) + return s; + + p = s + strlen(s); + while (p > s && isspace((int)p[-1])) + *(--p) = '\0'; + while (isspace((int)*s)) + s++; + + return s; +} + +/** + * Check for another complete text line, trim, return consumed char count. + * + * @param[in] s The input text, current read position. + * @param[in] l The input text, remaining available characters. + * @param[out] next Position after the current text line. + * @param[out] taken Count of consumed chars in current text line. + * + * @return Start of trimmed and NUL terminated text line. + * Or #NULL when no text line was found. + * + * Checks for the availability of another text line of input data. + * Manipulates the caller's input text in place. + * + * The end-of-line condition is the LF character ('\n'). Which covers + * LF-only as well as CR/LF input data. CR-only and LF/CR are considered + * unpopular and are not supported. LF/CR may appear to work at the + * caller's when leading whitespace gets trimmed (line boundaries will + * be incorrect, but content may get processed as expected). Support for + * all of the above combinations breaks the detection of empty lines (or + * becomes unmaintainably complex). + * + * The input buffer must be end-of-line terminated, lack of EOL results + * in failure to detect the text line. This is motivated by accumulating + * input in chunks, and the desire to not process incomplete lines before + * their reception has completed. Callers should enforce EOL if their + * source of input provides an EOF condition and is unreliable in terms + * of text line termination. + * + * When another text line is available, it gets NUL terminated and + * space gets trimmed of both ends. The start position of the trimmed + * text line is returned. Optionally the number of consumed characters + * is returned to the caller. Optionally 'next' points to after the + * returned text line, or #NULL when no other text is available in the + * input buffer. + * + * The 'taken' value is not preset by this routine, only gets updated. + * This is convenient for callers which expect to find multiple text + * lines in a received chunk, before finally discarding processed data + * from the input buffer (which can involve expensive memory move + * operations, and may be desirable to defer as much as possible). + * + * @since 0.6.0 + */ +SR_API char *sr_text_next_line(char *s, size_t l, char **next, size_t *taken) +{ + char *p; + + if (next) + *next = NULL; + if (!l) + l = strlen(s); + + /* Immediate reject incomplete input data. */ + if (!s || !*s || !l) + return NULL; + + /* Search for the next line termination. NUL terminate. */ + p = g_strstr_len(s, l, "\n"); + if (!p) + return NULL; + *p++ = '\0'; + if (taken) + *taken += p - s; + l -= p - s; + if (next) + *next = l ? p : NULL; + + /* Trim NUL terminated text line at both ends. */ + s = sr_text_trim_spaces(s); + return s; +} + +/** + * Isolates another space separated word in a text line. + * + * @param[in] s The input text, current read position. + * @param[out] next The position after the current word. + * + * @return The start of the current word. Or #NULL if there is none. + * + * Advances over leading whitespace. Isolates (NUL terminates) the next + * whitespace separated word. Optionally returns the position after the + * current word. Manipulates the caller's input text in place. + * + * @since 0.6.0 + */ +SR_API char *sr_text_next_word(char *s, char **next) +{ + char *word, *p; + + word = s; + if (next) + *next = NULL; + + /* Immediately reject incomplete input data. */ + if (!word || !*word) + return NULL; + + /* Advance over optional leading whitespace. */ + while (isspace((int)*word)) + word++; + if (!*word) + return NULL; + + /* + * Advance until whitespace or end of text. Quick return when + * end of input is seen. Otherwise advance over whitespace and + * return the position of trailing text. + */ + p = word; + while (*p && !isspace((int)*p)) + p++; + if (!*p) + return word; + *p++ = '\0'; + while (isspace((int)*p)) + p++; + if (!*p) + return word; + if (next) + *next = p; + return word; +} + +/** + * Get the number of necessary bits to hold a given value. Also gets + * the next power-of-two value at or above the caller provided value. + * + * @param[in] value The value that must get stored. + * @param[out] bits The required number of bits. + * @param[out] power The corresponding power-of-two. + * + * @return SR_OK upon success, SR_ERR* otherwise. + * + * TODO Move this routine to a more appropriate location, it is not + * strictly string related. + * + * @since 0.6.0 + */ +SR_API int sr_next_power_of_two(size_t value, size_t *bits, size_t *power) +{ + size_t need_bits; + size_t check_mask; + + if (bits) + *bits = 0; + if (power) + *power = 0; + + /* + * Handle the special case of input value 0 (needs 1 bit + * and results in "power of two" value 1) here. It is not + * covered by the generic logic below. + */ + if (!value) { + if (bits) + *bits = 1; + if (power) + *power = 1; + return SR_OK; + } + + need_bits = 0; + check_mask = 0; + do { + need_bits++; + check_mask <<= 1; + check_mask |= 1UL << 0; + } while (value & ~check_mask); + + if (bits) + *bits = need_bits; + if (power) + *power = ++check_mask; + return SR_OK; +} + /** @} */