Line data Source code
1 : #ifndef HEADER_fd_src_util_cstr_fd_cstr_h
2 : #define HEADER_fd_src_util_cstr_fd_cstr_h
3 :
4 : /* APIs for manipulating '\0'-terminated character strings ("cstr") */
5 :
6 : #include "../bits/fd_bits.h"
7 :
8 : FD_PROTOTYPES_BEGIN
9 :
10 : /* cstr input *********************************************************/
11 :
12 : /* fd_cstr_to_T converts the cstr pointed at by s into a T and returns
13 : its value. Caller promises s is non-NULL and points at a cstr.
14 :
15 : Note fd_cstr_to_cstr just returns s. As such the lifetime of the
16 : returned pointer is the lifetime s and ownership model of the
17 : underlying s is defined by the application.
18 :
19 : fd_cstr_to_char just returns the first character of the cstr (if cstr
20 : is the empty string, this will be the '\0' character ... otherwise,
21 : it will be a normal string character). As char do not have a
22 : consistent interpretation between platforms due to issues with the
23 : language standard itself, the value here should just be treated as a
24 : character and not an integer. Use fd_cstr_schar/fd_cstr_uchar if you
25 : need to treat a char as an integer.
26 :
27 : fd_cstr_to_cstr and fd_cstr_to_char exist primarily for type system
28 : completeness / facilitate various generic programming practices.
29 :
30 : The integer converters work in the strtol sense with base 0 (and thus
31 : ignore leading whitespace, handle leading signs and assume octal if
32 : the body is prefixed with 0, hexadecimal if prefixed with 0x and
33 : decimal otherwise). */
34 :
35 : FD_FN_CONST char const * fd_cstr_to_cstr ( char const * s );
36 : FD_FN_PURE char fd_cstr_to_char ( char const * s );
37 : FD_FN_PURE schar fd_cstr_to_schar ( char const * s );
38 : FD_FN_PURE short fd_cstr_to_short ( char const * s );
39 : FD_FN_PURE int fd_cstr_to_int ( char const * s );
40 : FD_FN_PURE long fd_cstr_to_long ( char const * s );
41 : FD_FN_PURE uchar fd_cstr_to_uchar ( char const * s );
42 : FD_FN_PURE ushort fd_cstr_to_ushort( char const * s );
43 : FD_FN_PURE uint fd_cstr_to_uint ( char const * s );
44 : FD_FN_PURE ulong fd_cstr_to_ulong ( char const * s );
45 : FD_FN_PURE float fd_cstr_to_float ( char const * s );
46 : #if FD_HAS_DOUBLE
47 : FD_FN_PURE double fd_cstr_to_double( char const * s );
48 : #endif
49 :
50 : /* fd_cstr_to_ulong_octal is the same as fd_cstr_to_ulong but assumes s
51 : points is octal. This is mostly used when dealing parsing UNIX style
52 : file permissions. */
53 :
54 : FD_FN_PURE ulong fd_cstr_to_ulong_octal( char const * s );
55 :
56 : /* fd_cstr_to_ulong_seq populates seq (which has room for seq max items)
57 : with the sequenced specified by the given cstr. Sequences are a
58 : comma separated list of ranges (e.g. "R0,R1,R2"). The ranges
59 : themselves can be themselves be individual integers (e.g. "5") or a
60 : simple range (e.g. "4-8", includes both endpoints, stop should be at
61 : least start), a range with a skip (e.g. "1-10/3" or "1-10:3", stop
62 : should be at least start and stride should be positive). Ignores
63 : internal whitespace. Robust against overflow / wrapping of ranges
64 : against ULONG_MAX. Items may appear in multiple times and sequences
65 : can have an arbitrary order. Caller promises seq is non-NULL if max
66 : is non-zero. Returns 0 on NULL or malformed cstr or empty sequence
67 : (seq contents might have been arbitrarily clobbered on a malformed
68 : cstr). */
69 :
70 : ulong /* Actual sequence length, if greater than seq_max returned sequence truncated. */
71 : fd_cstr_to_ulong_seq( char const * cstr, /* String to parse, NULL returns 0 */
72 : ulong * seq, /* Indexed [0,max), elements [0,min(actual sequence length,seq_max)) populated with
73 : the leading portion of the seq. Any remaining elements of seq are untouched. */
74 : ulong seq_max ); /* Maximum sequence length */
75 :
76 : /* fd_cstr_hash hashes the cstr pointed to by key to a ulong.
77 : fd_cstr_hash_append updates the hash value (it will be as though the
78 : fd_cstr_hash was called on the string concatenation of the all the
79 : keys provided to hash / hash append in order). Treats key==NULL the
80 : same as the empty string "". Yields identical cross platform results
81 : regardless of how the platform treats the sign of char. Based on one
82 : of the djb2 hash variants (public domain).
83 :
84 : FIXME: This is simple and fast and pretty good practically for string
85 : hashing but more robust and faster algos are probably out there. */
86 :
87 : FD_FN_PURE static inline ulong
88 : fd_cstr_hash_append( ulong hash,
89 10463785 : char const * key ) {
90 10463788 : if( FD_LIKELY( key ) ) {
91 10463787 : uchar const * p = (uchar const *)key;
92 404832935 : for(;;) {
93 404832935 : ulong c = p[0];
94 404832935 : if( FD_UNLIKELY( !c ) ) break;
95 394369144 : hash = (hash*33UL) ^ c;
96 394369144 : p++;
97 394369144 : }
98 10463787 : }
99 10463785 : return hash;
100 10463785 : }
101 :
102 291 : FD_FN_PURE static inline ulong fd_cstr_hash( char const * key ) { return fd_cstr_hash_append( 5381UL, key ); }
103 :
104 : /* fd_cstr_casecmp is equivalent to strcasecmp but doesn't require
105 : FD_HAS_HOSTED (POSIX) support. */
106 :
107 : FD_FN_PURE int
108 : fd_cstr_casecmp( char const * a,
109 : char const * b );
110 :
111 : /* fd_cstr_nlen is equivalent to strnlen but doesn't require
112 : FD_HAS_HOSTED (POSIX) support. */
113 :
114 : FD_FN_PURE ulong
115 : fd_cstr_nlen( char const * s,
116 : ulong m );
117 :
118 : /* fd_cstr_ncpy is a safe version of strncpy. d is the destination cstr
119 : and s is the source cstr. d and s should not overlap. Assumes d has
120 : space for up to m bytes. Always returns d. All bytes of d will be
121 : initialized. Further, if m is not zero, d will _always_ be properly
122 : '\0' terminated.
123 :
124 : Specifically, if m is 0 (i.e. d has zero bytes of storage), this
125 : returns d. Otherwise, if s is NULL, this will zero out all m bytes
126 : of d and return d. Otherwise, this will copy up to m-1 of the
127 : leading non-zero bytes in s into d. All remaining bytes of d (there
128 : will be at least 1) will be initialized to zero. */
129 :
130 : char *
131 : fd_cstr_ncpy( char * d,
132 : char const * s,
133 : ulong m );
134 :
135 : /* cstr output ********************************************************/
136 :
137 : /* fd_cstr_printf printf a cstr into the sz byte memory region pointed
138 : to by buf. Always returns buf.
139 :
140 : If buf is non-NULL and sz is non-zero, on return, buf will point to a
141 : cstr such that strlen(buf)<sz. That is, bytes [0,strlen(buf)] will
142 : be non-'\0', byte strlen(buf) will be '\0' and bytes (len,sz) will be
143 : unchanged. If more than sz bytes are needed to hold the requested
144 : cstr, the cstr will be truncated to its leading bytes such that
145 : strlen(buf)==sz-1. If opt_len is non-NULL, *opt_len will be set to
146 : the strlen(buf) on return.
147 :
148 : buf==NULL and/or sz==0UL are treated as a no-op. (If opt_len is
149 : non-NULL *opt_len wll be 0UL on return ... this is debatable though
150 : given the strlen(buf) property above. Might be better to this case
151 : as U.B., or abort if opt_len is requested when buf==NULL and sz==NULL
152 : or return ULONG_MAX in opt_len (-1) to indicate ill defined usage or
153 : ...) */
154 :
155 : char *
156 : fd_cstr_printf( char * buf,
157 : ulong sz,
158 : ulong * opt_len,
159 : char const * fmt, ... ) __attribute__((format(printf,4,5)));
160 :
161 : /* fd_cstr_printf_check is the same as fd_cstr_printf except that it
162 : returns 1 if the entire cstr, including the NUL terminating
163 : character was written to buf and 0 otherwise.
164 :
165 : If the cstr was truncated, or there was an error in the printf
166 : formatting process, 0 will be returned. Otherwise, on success, 1
167 : will be returned. If zero bytes are written to buf because the
168 : format string is empty, the return value will be 1. */
169 :
170 : int
171 : fd_cstr_printf_check( char * buf,
172 : ulong sz,
173 : ulong * opt_len,
174 : char const * fmt, ... ) __attribute__((format(printf,4,5)));
175 :
176 : /* fd_cstr_init start writing a cstr into buf. Returns where the first
177 : character of the cstr should be written (==buf). */
178 :
179 2748662 : static inline char * fd_cstr_init( char * buf ) { return buf; }
180 :
181 : /* fd_cstr_fini finished writing a cstr to buf. Assumes p is valid
182 : (non-NULL and room for the terminating '\0'). At this point, the buf
183 : passed to fd_cstr_init will be properly '\0' terminated. */
184 :
185 2748764 : static inline void fd_cstr_fini( char * p ) { *p = '\0'; }
186 :
187 : /* fd_cstr_append_char append character c to cstr. Assumes p is valid
188 : (non-NULL and room for at least this char and a final terminating
189 : '\0') and c is not '\0' */
190 :
191 16846412 : static inline char * fd_cstr_append_char( char * p, char c ) { *(p++) = c; return p; }
192 :
193 : /* fd_cstr_append_text appends n characters of text pointed to by t to
194 : p. Assumes p is valid (non-NULL and room for at least n characters
195 : and a final terminating '\0') and t is valid (points to n consecutive
196 : non-'\0' characters). n is zero is fine. */
197 :
198 : static inline char *
199 : fd_cstr_append_text( char * p,
200 : char const * t,
201 2400467 : ulong n ) {
202 2400467 : fd_memcpy( p, t, n );
203 2400467 : return p + n;
204 2400467 : }
205 :
206 : /* fd_cstr_append_cstr appends the cstr pointed to by s to p. Assumes p
207 : is valid (non-NULL and room for at least strlen( s ) characters and a
208 : final terminating '\0'). s==NULL is treated as a no-op. */
209 :
210 : static inline char *
211 : fd_cstr_append_cstr( char * p,
212 49215 : char const * s ) {
213 49215 : if( FD_UNLIKELY( !s ) ) return p;
214 49215 : ulong n = strlen( s );
215 49215 : fd_memcpy( p, s, n );
216 49215 : return p + n;
217 49215 : }
218 :
219 : /* fd_cstr_append_cstr_safe appends up to n chars of the cstr pointed
220 : to by to p. Assumes p is valid (non-NULL and room for at least n
221 : characters and a final terminating '\0'). s==NULL is treated as a
222 : no-op. */
223 :
224 : static inline char *
225 : fd_cstr_append_cstr_safe( char * p,
226 : char const * s,
227 10031 : ulong n ) {
228 10031 : if( FD_UNLIKELY( !s ) ) return p;
229 10031 : ulong l = fd_ulong_min( strlen( s ), n );
230 10031 : fd_memcpy( p, s, l );
231 10031 : return p + l;
232 10031 : }
233 :
234 : /* fd_cstr_append_printf appends the printf of the fmt string into p.
235 : Assumes p is valid (non-NULL and room for printf characters and a
236 : final terminating '\0'). */
237 :
238 : char *
239 : fd_cstr_append_printf( char * p,
240 : char const * fmt, ... ) __attribute__((format(printf,2,3)));
241 :
242 : /* fd_cstr_append_ulong_as_text pretty prints the ulong into p (and
243 : similarly for the other unsigned integer types). Assumes p is valid
244 : (non-NULL and room for at least n characters and a final terminating
245 : '\0'), x is small enough to pretty print to n chars (which implies
246 : that n is at least 1). ws is the character to left pad the converted
247 : value with. pm is prefix character to use (e.g. '+', '-'), '\0'
248 : indicates no prefix. If a prefix is requested, it will be
249 : immediately before the most significant converted character.
250 :
251 : fd_cstr_append_ulong_as_hex is the base-16 equivalent of the above. */
252 :
253 : static inline char *
254 : fd_cstr_append_uint_as_text( char * p,
255 : char ws,
256 : char pm,
257 : uint x,
258 14394941 : ulong n ) {
259 14394941 : char * p0 = p;
260 14394941 : p += n;
261 14394941 : char * q = p;
262 28790974 : do { uint d = x % 10U; x /= 10U; *(--q) = (char)( d + (uint)'0' ); } while( x );
263 14394941 : if( pm ) *(--q) = pm;
264 19193338 : while( p0<q ) *(p0++) = ws;
265 14394941 : return p;
266 14394941 : }
267 :
268 : static inline char *
269 : fd_cstr_append_uint_as_hex( char * p,
270 : char ws,
271 : uint x,
272 0 : ulong n ) {
273 0 : static char const bin2hex[ 16 ] = {
274 0 : '0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'
275 0 : };
276 0 : char * p0 = p;
277 0 : p += n;
278 0 : char * q = p;
279 0 : do { uint d = x & 0xFU; x >>= 4; *(--q) = bin2hex[ d ]; } while( x );
280 0 : while( p0<q ) *(p0++) = ws;
281 0 : return p;
282 0 : }
283 :
284 : static inline char *
285 : fd_cstr_append_ulong_as_text( char * p,
286 : char ws,
287 : char pm,
288 : ulong x,
289 813 : ulong n ) {
290 813 : char * p0 = p;
291 813 : p += n;
292 813 : char * q = p;
293 7143 : do { ulong d = x % 10UL; x /= 10UL; *(--q) = (char)( d + (ulong)'0' ); } while( x );
294 813 : if( pm ) *(--q) = pm;
295 1065 : while( p0<q ) *(p0++) = ws;
296 813 : return p;
297 813 : }
298 :
299 : static inline char *
300 : fd_cstr_append_ulong_as_hex( char * p,
301 : char ws,
302 : ulong x,
303 0 : ulong n ) {
304 0 : static char const bin2hex[ 16 ] = {
305 0 : '0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'
306 0 : };
307 0 : char * p0 = p;
308 0 : p += n;
309 0 : char * q = p;
310 0 : do { ulong d = x & 0xFUL; x >>= 4; *(--q) = bin2hex[ d ]; } while( x );
311 0 : while( p0<q ) *(p0++) = ws;
312 0 : return p;
313 0 : }
314 :
315 : #if FD_HAS_INT128
316 :
317 : static inline char *
318 : fd_cstr_append_uint128_as_text( char * p,
319 : char ws,
320 : char pm,
321 : uint128 x,
322 960 : ulong n ) {
323 960 : char * p0 = p;
324 960 : p += n;
325 960 : char * q = p;
326 19200 : do { uint128 d = x % (uint128)10UL; x /= (uint128)10UL; *(--q) = (char)( d + (uint128)'0' ); } while( x );
327 960 : if( pm ) *(--q) = pm;
328 1440 : while( p0<q ) *(p0++) = ws;
329 960 : return p;
330 960 : }
331 :
332 : static inline char *
333 : fd_cstr_append_uint128_as_hex( char * p,
334 : char ws,
335 : ulong x,
336 0 : ulong n ) {
337 0 : static char const bin2hex[ 16 ] = {
338 0 : '0','1','2','3','4','5','6','7','8','9','a','b','c','d','e','f'
339 0 : };
340 0 : char * p0 = p;
341 0 : p += n;
342 0 : char * q = p;
343 0 : do { uint d = (uint)x & 0xFU; x >>= 4; *(--q) = bin2hex[ d ]; } while( x );
344 0 : while( p0<q ) *(p0++) = ws;
345 0 : return p;
346 0 : }
347 :
348 : #endif
349 :
350 : static inline char *
351 : fd_cstr_append_uchar_as_text( char * p,
352 : char ws,
353 : char pm,
354 : uchar x,
355 96 : ulong n ) {
356 96 : return fd_cstr_append_uint_as_text( p, ws, pm, (uint)x, n );
357 96 : }
358 :
359 : static inline char *
360 : fd_cstr_append_ushort_as_text( char * p,
361 : char ws,
362 : char pm,
363 : ushort x,
364 150 : ulong n ) {
365 150 : return fd_cstr_append_uint_as_text( p, ws, pm, (uint)x, n );
366 150 : }
367 :
368 : /* fd_cstr_append_fxp10_as_text same as the above but for the decimal
369 : fixed point value:
370 : x / 10^f
371 : Assumes p is valid (non-NULL and room for at least n characters and a
372 : final terminating '\0'), x / 10^f is not too large to fit within n
373 : characters (which implies that n is at least f+2). ws is the
374 : character to left pad the converted value with. pm is prefix
375 : character to use (e.g. '+', '-'), '\0' indicates no prefix. If a
376 : prefix is requested, it will be immediately before the most
377 : significant converted character. */
378 :
379 : FD_FN_UNUSED static char * /* Work around -Winline */
380 : fd_cstr_append_fxp10_as_text( char * p,
381 : char ws,
382 : char pm,
383 : ulong f,
384 : ulong x,
385 2401089 : ulong n ) {
386 2401089 : char * p0 = p;
387 2401089 : p += n;
388 2401089 : char * q = p;
389 24001818 : while( f ) { ulong d = x % 10UL; x /= 10UL; *(--q) = (char)( d + (ulong)'0' ); f--; }
390 2401089 : *(--q) = '.';
391 4738643 : do { ulong d = x % 10UL; x /= 10UL; *(--q) = (char)( d + (ulong)'0' ); } while( x );
392 2401089 : if( pm ) *(--q) = pm;
393 2475424 : while( p0<q ) *(p0++) = ws;
394 2401089 : return p;
395 2401089 : }
396 :
397 : /* fd_cstr_tokenize tokenizes the cstr of the form whose first
398 : byte is pointed to by cstr:
399 :
400 : [WS][TOKEN 0][DELIM][WS][TOKEN 1][DELIM]...[WS][TOKEN N]{[DELIM][WS][NUL],[NUL]}
401 :
402 : in-place, into:
403 :
404 : [WS][TOKEN 0][NUL][WS][TOKEN 1][NUL]...[WS][TOKEN tok_cnt-1][NUL]
405 :
406 : and returns tok_cnt.
407 :
408 : Further, on return, tok[i] for i in [0,min(tok_cnt,tok_max)) where
409 : tok_cnt is the number of tokens in cstr will point to the first
410 : byte of each token. Due to the tokenization, each one of these will
411 : be properly '\0' terminated.
412 :
413 : Above, [WS] is a sequence of zero or more whitespace characters,
414 : [TOKEN *] are a sequence of zero or more non-delim and non-NUL
415 : characters and delim is assumed to be a non-NUL non-whitespace
416 : character (e.g. ',').
417 :
418 : As such:
419 : - The original cstr is clobbered by this call.
420 : - tok[*] point to a properly terminated cstr into the original cstr
421 : on return. They thus have the same lifetime issues as the original
422 : cstr.
423 : - If tok_cnt > tok_max, tok wasn't large enough to hold all the
424 : tokens found in the cstr. Only the first max are available in
425 : tok[*] (the entire string was still tokenized though).
426 : - Found tokens will not have any leading whitespace.
427 : - Found tokens might have internal or trailing whitespace.
428 : - Zero length tokens are possible. E.g. assuming delim==':', the cstr
429 : "a: b::d: :f" has the tokens: "a", "b", "", "d", "", "f".
430 : - If the final token is zero length, it should use an explicit
431 : delimiter. E.g. assuming delim=='|':
432 : "a|b" has tokens "a", "b"
433 : "a|b|" has tokens "a", "b"
434 : "a|b| " has tokens "a", "b"
435 : "a|b||" has tokens "a", "b", ""
436 : "a|b| |" has tokens "a", "b", ""
437 : "a|b| | " has tokens "a", "b", ""
438 : - This is also true if the final token is the initial token. E.g.
439 : assuming delim==';':
440 : "" has no tokens
441 : " " has no tokens
442 : ";" has the token ""
443 : " ;" has the token ""
444 : " ; " has the token "" */
445 :
446 : ulong
447 : fd_cstr_tokenize( char ** tok,
448 : ulong tok_max,
449 : char * cstr,
450 : char delim );
451 :
452 : /* fd_cstr_append_utf8 appends the UTF-8 encoding of a Unicode code
453 : point into p. Assumes p is valid (non-NULL and room for 1-4 chars
454 : and a final terminating '\0'). */
455 :
456 : static inline char *
457 : fd_cstr_append_utf8( char * p,
458 411 : uint rune ) {
459 411 : if( FD_LIKELY( rune<=0x7f ) ) {
460 387 : *(p++) = (char)rune;
461 387 : } else if( rune<=0x7ff ) {
462 6 : *(p++) = (char)( 0xc0 | (rune>>6) );
463 6 : *(p++) = (char)( 0x80 | ((rune )&0x3f) );
464 18 : } else if( rune<=0xffff ) {
465 12 : *(p++) = (char)( 0xe0 | (rune>>12) );
466 12 : *(p++) = (char)( 0x80 | ((rune>> 6)&0x3f) );
467 12 : *(p++) = (char)( 0x80 | ((rune )&0x3f) );
468 12 : } else if( rune<=0x10ffff ) {
469 6 : *(p++) = (char)( 0xf0 | (rune>>18) );
470 6 : *(p++) = (char)( 0x80 | ((rune>>12)&0x3f) );
471 6 : *(p++) = (char)( 0x80 | ((rune>> 6)&0x3f) );
472 6 : *(p++) = (char)( 0x80 | (rune &0x3f) );
473 6 : } else {
474 : /* replacement char */
475 0 : *(p++) = (char)0xef;
476 0 : *(p++) = (char)0xbf;
477 0 : *(p++) = (char)0xbd;
478 0 : }
479 411 : return p;
480 411 : }
481 :
482 : FD_PROTOTYPES_END
483 :
484 : /* The below macros guarantee the corresponding ctype.h functions return
485 : a value strictly in [0,1]. These still need the caller to include
486 : <ctype.h> to use.
487 :
488 : For context, the vast majority of developers reasonably expect
489 : ctype.h functions (like isspace) return 1/0 if the given character
490 : is/is not in the tested class.
491 :
492 : But the standard actually says these functions return non-zero/0.
493 :
494 : Many common standard libraries exploit this ambiguity. For example,
495 : isspace('\n') returns 8192 on recent linux-gcc-x86_64.
496 :
497 : In most usage, this subtle distinction does not make a difference.
498 :
499 : That makes it worse.
500 :
501 : When the distinction matters, it is incredibly difficult and time
502 : consuming to debug. Consider using ctype.h functions in user input
503 : parsing to compute the case of a switch statement. Suddenly,
504 : seemingly innocous code run on seemingly innocous input generates a
505 : branch to a mystifying place. Hilarity ensues.
506 :
507 : Worse still, this is a massive security risk. Consider using ctype.h
508 : in a mission critical VM implementation. Strictly deterministic
509 : verifiable cross platform behavior is a necessity. Should a
510 : malicious smart contract be able to halt a chain because the standard
511 : absent mindedly gave isspace dubious flexibility on its return value?
512 :
513 : That is, this behavior is absolutely vile.
514 :
515 : This is an example of "implicitly specified behavior". There is some
516 : behavior that must followed bit-for-bit. But nobody knows what that
517 : behavior is because the standard was written poorly. And then the
518 : library implementation blindly followed the standard and ignored
519 : developer usability in ambiguous situations.
520 :
521 : This also violates the core UNIX principle (and generally good idea
522 : in engineering or beyond) of "be generous in what you accept and
523 : strict in what you produce". Given the generally absymal language
524 : handling of boolean values, the only practical way to handle
525 : true/false reliably and efficiently is accept non-zero/0 but only
526 : produce 1/0. (And, since both sides have some responsibility in the
527 : examples above, the library implementation is more at fault as it is
528 : more foundational, more trusted and more reused. Library code should
529 : be held to a higher standard than code built on top of it.)
530 :
531 : Any use of naked ctype.h functions should be considered harmful and
532 : eradicated with extreme prejudice until the language and standard
533 : library implementations get a clue (not holding my breath).
534 :
535 : TL;DR ctype.h but sane. */
536 :
537 3492111 : #define fd_isalnum(c) (!!isalnum((c)))
538 768 : #define fd_isalpha(c) (!!isalpha((c)))
539 768 : #define fd_iscntrl(c) (!!iscntrl((c)))
540 182988 : #define fd_isdigit(c) (!!isdigit((c)))
541 768 : #define fd_isgraph(c) (!!isgraph((c)))
542 768 : #define fd_islower(c) (!!islower((c)))
543 768 : #define fd_isprint(c) (!!isprint((c)))
544 3152973 : #define fd_ispunct(c) (!!ispunct((c)))
545 336954 : #define fd_isspace(c) (!!isspace((c)))
546 768 : #define fd_isupper(c) (!!isupper((c)))
547 192369 : #define fd_isxdigit(c) (!!isxdigit((c)))
548 768 : #define fd_isascii(c) (!!isascii((c)))
549 768 : #define fd_isblank(c) (!!isblank((c)))
550 :
551 : #endif /* HEADER_fd_src_util_cstr_fd_cstr_h */
|