fix: move static functions inside ifdef
[babeltrace.git] / src / common / common.c
CommitLineData
1670bffd
PP
1/*
2 * Babeltrace common functions
3 *
4 * Copyright 2016 Philippe Proulx <pproulx@efficios.com>
5 *
6 * Permission is hereby granted, free of charge, to any person obtaining a copy
7 * of this software and associated documentation files (the "Software"), to deal
8 * in the Software without restriction, including without limitation the rights
9 * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
10 * copies of the Software, and to permit persons to whom the Software is
11 * furnished to do so, subject to the following conditions:
12 *
13 * The above copyright notice and this permission notice shall be included in
14 * all copies or substantial portions of the Software.
15 *
16 * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
17 * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
18 * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
19 * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
20 * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
21 * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
22 * SOFTWARE.
23 */
24
86d8b7b8 25#define BT_LOG_OUTPUT_LEVEL log_level
b4565e8b 26#define BT_LOG_TAG "COMMON"
86d8b7b8 27#include "logging/log.h"
b4565e8b 28
290725f7 29#include <unistd.h>
1670bffd 30#include <string.h>
85cd02cf 31#include <inttypes.h>
1670bffd 32#include <sys/types.h>
fcc7e8dd 33#include <sys/stat.h>
1670bffd 34#include <unistd.h>
578e048b 35#include "common/assert.h"
85cd02cf 36#include <stdarg.h>
db0f160a 37#include <ctype.h>
1670bffd 38#include <glib.h>
108e5a1e 39#include <stdlib.h>
85cd02cf
PP
40#include <stdio.h>
41#include <wchar.h>
fcc7e8dd 42#include <stdbool.h>
91d81473 43#include "common/macros.h"
578e048b
MJ
44#include "common/common.h"
45#include "compat/unistd.h"
1670bffd 46
2006c005
MJ
47#ifndef __MINGW32__
48#include <pwd.h>
49#endif
50
d9676d8c 51#define SYSTEM_PLUGIN_PATH INSTALL_LIBDIR "/babeltrace2/plugins"
1670bffd 52#define HOME_ENV_VAR "HOME"
d9676d8c 53#define HOME_PLUGIN_SUBPATH "/.local/lib/babeltrace2/plugins"
1670bffd 54
6c4a0731
PP
55static const char *bt_common_color_code_reset = "";
56static const char *bt_common_color_code_bold = "";
57static const char *bt_common_color_code_fg_default = "";
58static const char *bt_common_color_code_fg_red = "";
59static const char *bt_common_color_code_fg_green = "";
60static const char *bt_common_color_code_fg_yellow = "";
61static const char *bt_common_color_code_fg_blue = "";
62static const char *bt_common_color_code_fg_magenta = "";
63static const char *bt_common_color_code_fg_cyan = "";
64static const char *bt_common_color_code_fg_light_gray = "";
65static const char *bt_common_color_code_bg_default = "";
66static const char *bt_common_color_code_bg_red = "";
67static const char *bt_common_color_code_bg_green = "";
68static const char *bt_common_color_code_bg_yellow = "";
69static const char *bt_common_color_code_bg_blue = "";
70static const char *bt_common_color_code_bg_magenta = "";
71static const char *bt_common_color_code_bg_cyan = "";
72static const char *bt_common_color_code_bg_light_gray = "";
73
74static
75void __attribute__((constructor)) bt_common_color_ctor(void)
76{
77 if (bt_common_colors_supported()) {
78 bt_common_color_code_reset = BT_COMMON_COLOR_RESET;
79 bt_common_color_code_bold = BT_COMMON_COLOR_BOLD;
80 bt_common_color_code_fg_default = BT_COMMON_COLOR_FG_DEFAULT;
81 bt_common_color_code_fg_red = BT_COMMON_COLOR_FG_RED;
82 bt_common_color_code_fg_green = BT_COMMON_COLOR_FG_GREEN;
83 bt_common_color_code_fg_yellow = BT_COMMON_COLOR_FG_YELLOW;
84 bt_common_color_code_fg_blue = BT_COMMON_COLOR_FG_BLUE;
85 bt_common_color_code_fg_magenta = BT_COMMON_COLOR_FG_MAGENTA;
86 bt_common_color_code_fg_cyan = BT_COMMON_COLOR_FG_CYAN;
87 bt_common_color_code_fg_light_gray = BT_COMMON_COLOR_FG_LIGHT_GRAY;
88 bt_common_color_code_bg_default = BT_COMMON_COLOR_BG_DEFAULT;
89 bt_common_color_code_bg_red = BT_COMMON_COLOR_BG_RED;
90 bt_common_color_code_bg_green = BT_COMMON_COLOR_BG_GREEN;
91 bt_common_color_code_bg_yellow = BT_COMMON_COLOR_BG_YELLOW;
92 bt_common_color_code_bg_blue = BT_COMMON_COLOR_BG_BLUE;
93 bt_common_color_code_bg_magenta = BT_COMMON_COLOR_BG_MAGENTA;
94 bt_common_color_code_bg_cyan = BT_COMMON_COLOR_BG_CYAN;
95 bt_common_color_code_bg_light_gray = BT_COMMON_COLOR_BG_LIGHT_GRAY;
96 }
97}
98
1670bffd
PP
99BT_HIDDEN
100const char *bt_common_get_system_plugin_path(void)
101{
102 return SYSTEM_PLUGIN_PATH;
103}
104
a613ad68
MJ
105#ifdef __MINGW32__
106BT_HIDDEN
107bool bt_common_is_setuid_setgid(void)
108{
109 return false;
110}
111#else /* __MINGW32__ */
1670bffd
PP
112BT_HIDDEN
113bool bt_common_is_setuid_setgid(void)
114{
115 return (geteuid() != getuid() || getegid() != getgid());
116}
a613ad68 117#endif /* __MINGW32__ */
1670bffd 118
3a5e626b
MJ
119#ifdef __MINGW32__
120static
121const char *bt_get_home_dir(int log_level)
122{
123 return g_get_home_dir();
124}
125#else /* __MINGW32__ */
2006c005 126static
86d8b7b8 127char *bt_secure_getenv(const char *name, int log_level)
1670bffd
PP
128{
129 if (bt_common_is_setuid_setgid()) {
b4565e8b
PP
130 BT_LOGD("Disregarding environment variable for setuid/setgid binary: "
131 "name=\"%s\"", name);
1670bffd
PP
132 return NULL;
133 }
134 return getenv(name);
135}
136
2006c005 137static
86d8b7b8 138const char *bt_get_home_dir(int log_level)
1670bffd
PP
139{
140 char *val = NULL;
141 struct passwd *pwd;
142
86d8b7b8 143 val = bt_secure_getenv(HOME_ENV_VAR, log_level);
1670bffd
PP
144 if (val) {
145 goto end;
146 }
147 /* Fallback on password file. */
148 pwd = getpwuid(getuid());
149 if (!pwd) {
150 goto end;
151 }
152 val = pwd->pw_dir;
153end:
154 return val;
155}
2006c005 156#endif /* __MINGW32__ */
1670bffd
PP
157
158BT_HIDDEN
86d8b7b8 159char *bt_common_get_home_plugin_path(int log_level)
1670bffd
PP
160{
161 char *path = NULL;
162 const char *home_dir;
b4565e8b 163 size_t length;
1670bffd 164
86d8b7b8 165 home_dir = bt_get_home_dir(log_level);
1670bffd
PP
166 if (!home_dir) {
167 goto end;
168 }
169
b4565e8b
PP
170 length = strlen(home_dir) + strlen(HOME_PLUGIN_SUBPATH) + 1;
171
172 if (length >= PATH_MAX) {
3f7d4d90
PP
173 BT_LOGW("Home directory path is too long: "
174 "length=%zu, max-length=%u", length, PATH_MAX);
1670bffd
PP
175 goto end;
176 }
177
178 path = malloc(PATH_MAX);
179 if (!path) {
180 goto end;
181 }
182
183 strcpy(path, home_dir);
184 strcat(path, HOME_PLUGIN_SUBPATH);
185
186end:
187 return path;
188}
189
190BT_HIDDEN
191int bt_common_append_plugin_path_dirs(const char *paths, GPtrArray *dirs)
192{
193 int ret = 0;
194 const char *at;
195 const char *end;
196 size_t init_dirs_len;
197
f6ccaed9 198 BT_ASSERT(dirs);
1670bffd
PP
199 init_dirs_len = dirs->len;
200
201 if (!paths) {
202 /* Nothing to append */
203 goto end;
204 }
205
206 at = paths;
207 end = paths + strlen(paths);
208
209 while (at < end) {
210 GString *path;
52238017 211 const char *next_sep;
1670bffd 212
52238017
MJ
213 next_sep = strchr(at, G_SEARCHPATH_SEPARATOR);
214 if (next_sep == at) {
1670bffd
PP
215 /*
216 * Empty path: try next character (supported
217 * to conform to the typical parsing of $PATH).
218 */
219 at++;
220 continue;
52238017
MJ
221 } else if (!next_sep) {
222 /* No more separator: use the remaining */
223 next_sep = paths + strlen(paths);
1670bffd
PP
224 }
225
226 path = g_string_new(NULL);
227 if (!path) {
228 goto error;
229 }
230
52238017
MJ
231 g_string_append_len(path, at, next_sep - at);
232 at = next_sep + 1;
1670bffd
PP
233 g_ptr_array_add(dirs, path);
234 }
235
236 goto end;
237
238error:
239 ret = -1;
240
241 /* Remove the new entries in dirs */
242 while (dirs->len > init_dirs_len) {
243 g_ptr_array_remove_index(dirs, init_dirs_len);
244 }
245
246end:
247 return ret;
248}
290725f7 249
fcc7e8dd
PP
250static
251bool isarealtty(int fd)
252{
253 bool istty = false;
254 struct stat tty_stats;
255
256 if (!isatty(fd)) {
257 /* Not a TTY */
258 goto end;
259 }
260
261 if (fstat(fd, &tty_stats) == 0) {
262 if (!S_ISCHR(tty_stats.st_mode)) {
263 /* Not a character device: not a TTY */
264 goto end;
265 }
266 }
267
268 istty = true;
269
270end:
271 return istty;
272}
273
ad96d936
PP
274BT_HIDDEN
275bool bt_common_colors_supported(void)
290725f7
PP
276{
277 static bool supports_colors = false;
278 static bool supports_colors_set = false;
fcc7e8dd
PP
279 const char *term_env_var;
280 const char *term_color_env_var;
290725f7
PP
281
282 if (supports_colors_set) {
283 goto end;
284 }
285
286 supports_colors_set = true;
287
fcc7e8dd
PP
288 /*
289 * `BABELTRACE_TERM_COLOR` environment variable always overrides
290 * the automatic color support detection.
291 */
292 term_color_env_var = getenv("BABELTRACE_TERM_COLOR");
293 if (term_color_env_var) {
294 if (g_ascii_strcasecmp(term_color_env_var, "always") == 0) {
295 /* Force colors */
296 supports_colors = true;
297 } else if (g_ascii_strcasecmp(term_color_env_var, "never") == 0) {
298 /* Force no colors */
299 goto end;
300 }
62128320
PP
301 }
302
fcc7e8dd
PP
303 /* We need a compatible, known terminal */
304 term_env_var = getenv("TERM");
305 if (!term_env_var) {
290725f7
PP
306 goto end;
307 }
308
fcc7e8dd
PP
309 if (strncmp(term_env_var, "xterm", 5) != 0 &&
310 strncmp(term_env_var, "rxvt", 4) != 0 &&
311 strncmp(term_env_var, "konsole", 7) != 0 &&
312 strncmp(term_env_var, "gnome", 5) != 0 &&
313 strncmp(term_env_var, "screen", 5) != 0 &&
314 strncmp(term_env_var, "tmux", 4) != 0 &&
315 strncmp(term_env_var, "putty", 5) != 0) {
290725f7
PP
316 goto end;
317 }
318
fcc7e8dd
PP
319 /* Both standard output and error streams need to be TTYs */
320 if (!isarealtty(STDOUT_FILENO) || !isarealtty(STDERR_FILENO)) {
290725f7
PP
321 goto end;
322 }
323
324 supports_colors = true;
325
326end:
327 return supports_colors;
328}
329
330BT_HIDDEN
331const char *bt_common_color_reset(void)
332{
6c4a0731 333 return bt_common_color_code_reset;
290725f7
PP
334}
335
336BT_HIDDEN
337const char *bt_common_color_bold(void)
338{
6c4a0731 339 return bt_common_color_code_bold;
290725f7
PP
340}
341
342BT_HIDDEN
343const char *bt_common_color_fg_default(void)
344{
6c4a0731 345 return bt_common_color_code_fg_default;
290725f7
PP
346}
347
348BT_HIDDEN
349const char *bt_common_color_fg_red(void)
350{
6c4a0731 351 return bt_common_color_code_fg_red;
290725f7
PP
352}
353
354BT_HIDDEN
355const char *bt_common_color_fg_green(void)
356{
6c4a0731 357 return bt_common_color_code_fg_green;
290725f7
PP
358}
359
360BT_HIDDEN
361const char *bt_common_color_fg_yellow(void)
362{
6c4a0731 363 return bt_common_color_code_fg_yellow;
290725f7
PP
364}
365
366BT_HIDDEN
367const char *bt_common_color_fg_blue(void)
368{
6c4a0731 369 return bt_common_color_code_fg_blue;
290725f7
PP
370}
371
372BT_HIDDEN
373const char *bt_common_color_fg_magenta(void)
374{
6c4a0731 375 return bt_common_color_code_fg_magenta;
290725f7
PP
376}
377
378BT_HIDDEN
379const char *bt_common_color_fg_cyan(void)
380{
6c4a0731 381 return bt_common_color_code_fg_cyan;
290725f7
PP
382}
383
384BT_HIDDEN
385const char *bt_common_color_fg_light_gray(void)
386{
6c4a0731 387 return bt_common_color_code_fg_light_gray;
290725f7
PP
388}
389
390BT_HIDDEN
391const char *bt_common_color_bg_default(void)
392{
6c4a0731 393 return bt_common_color_code_bg_default;
290725f7
PP
394}
395
396BT_HIDDEN
397const char *bt_common_color_bg_red(void)
398{
6c4a0731 399 return bt_common_color_code_bg_red;
290725f7
PP
400}
401
402BT_HIDDEN
403const char *bt_common_color_bg_green(void)
404{
6c4a0731 405 return bt_common_color_code_bg_green;
290725f7
PP
406}
407
408BT_HIDDEN
409const char *bt_common_color_bg_yellow(void)
410{
6c4a0731 411 return bt_common_color_code_bg_yellow;
290725f7
PP
412}
413
414BT_HIDDEN
415const char *bt_common_color_bg_blue(void)
416{
6c4a0731 417 return bt_common_color_code_bg_blue;
290725f7
PP
418}
419
420BT_HIDDEN
421const char *bt_common_color_bg_magenta(void)
422{
6c4a0731 423 return bt_common_color_code_bg_magenta;
290725f7
PP
424}
425
426BT_HIDDEN
427const char *bt_common_color_bg_cyan(void)
428{
6c4a0731 429 return bt_common_color_code_bg_cyan;
290725f7
PP
430}
431
432BT_HIDDEN
433const char *bt_common_color_bg_light_gray(void)
434{
6c4a0731 435 return bt_common_color_code_bg_light_gray;
290725f7 436}
db0f160a
PP
437
438BT_HIDDEN
439GString *bt_common_string_until(const char *input, const char *escapable_chars,
440 const char *end_chars, size_t *end_pos)
441{
442 GString *output = g_string_new(NULL);
443 const char *ch;
444 const char *es_char;
445 const char *end_char;
446
447 if (!output) {
448 goto error;
449 }
450
451 for (ch = input; *ch != '\0'; ch++) {
452 if (*ch == '\\') {
453 bool continue_loop = false;
454
455 if (ch[1] == '\0') {
456 /* `\` at the end of the string: append `\` */
457 g_string_append_c(output, *ch);
458 ch++;
459 goto set_end_pos;
460 }
461
462 for (es_char = escapable_chars; *es_char != '\0'; es_char++) {
463 if (ch[1] == *es_char) {
464 /*
465 * `\` followed by an escapable
466 * character: append the escaped
467 * character only.
468 */
469 g_string_append_c(output, ch[1]);
470 ch++;
471 continue_loop = true;
472 break;
473 }
474 }
475
476 if (continue_loop) {
477 continue;
478 }
479
480 /*
481 * `\` followed by a non-escapable character:
482 * append `\` and the character.
483 */
484 g_string_append_c(output, *ch);
485 g_string_append_c(output, ch[1]);
486 ch++;
487 continue;
488 } else {
489 for (end_char = end_chars; *end_char != '\0'; end_char++) {
490 if (*ch == *end_char) {
491 /*
492 * End character found:
493 * terminate this loop.
494 */
495 goto set_end_pos;
496 }
497 }
498
499 /* Normal character: append */
500 g_string_append_c(output, *ch);
501 }
502 }
503
504set_end_pos:
505 if (end_pos) {
506 *end_pos = ch - input;
507 }
508
509 goto end;
510
511error:
512 if (output) {
513 g_string_free(output, TRUE);
514 }
515
516end:
517 return output;
518}
519
520BT_HIDDEN
36b405c6 521GString *bt_common_shell_quote(const char *input, bool with_single_quotes)
db0f160a
PP
522{
523 GString *output = g_string_new(NULL);
524 const char *ch;
525 bool no_quote = true;
526
527 if (!output) {
528 goto end;
529 }
530
531 if (strlen(input) == 0) {
36b405c6
PP
532 if (with_single_quotes) {
533 g_string_assign(output, "''");
534 }
535
db0f160a
PP
536 goto end;
537 }
538
539 for (ch = input; *ch != '\0'; ch++) {
540 const char c = *ch;
541
542 if (!g_ascii_isalpha(c) && !g_ascii_isdigit(c) && c != '_' &&
543 c != '@' && c != '%' && c != '+' && c != '=' &&
544 c != ':' && c != ',' && c != '.' && c != '/' &&
545 c != '-') {
546 no_quote = false;
547 break;
548 }
549 }
550
551 if (no_quote) {
552 g_string_assign(output, input);
553 goto end;
554 }
555
36b405c6
PP
556 if (with_single_quotes) {
557 g_string_assign(output, "'");
558 }
db0f160a
PP
559
560 for (ch = input; *ch != '\0'; ch++) {
561 if (*ch == '\'') {
562 g_string_append(output, "'\"'\"'");
563 } else {
564 g_string_append_c(output, *ch);
565 }
566 }
567
36b405c6
PP
568 if (with_single_quotes) {
569 g_string_append_c(output, '\'');
570 }
db0f160a
PP
571
572end:
573 return output;
574}
575
576BT_HIDDEN
577bool bt_common_string_is_printable(const char *input)
578{
579 const char *ch;
580 bool printable = true;
f6ccaed9 581 BT_ASSERT(input);
db0f160a
PP
582
583 for (ch = input; *ch != '\0'; ch++) {
584 if (!isprint(*ch) && *ch != '\n' && *ch != '\r' &&
585 *ch != '\t' && *ch != '\v') {
586 printable = false;
587 goto end;
588 }
589 }
590
591end:
592 return printable;
593}
594
595BT_HIDDEN
596void bt_common_destroy_lttng_live_url_parts(
597 struct bt_common_lttng_live_url_parts *parts)
598{
599 if (!parts) {
600 goto end;
601 }
602
603 if (parts->proto) {
604 g_string_free(parts->proto, TRUE);
605 parts->proto = NULL;
606 }
607
608 if (parts->hostname) {
609 g_string_free(parts->hostname, TRUE);
610 parts->hostname = NULL;
611 }
612
613 if (parts->target_hostname) {
614 g_string_free(parts->target_hostname, TRUE);
615 parts->target_hostname = NULL;
616 }
617
618 if (parts->session_name) {
619 g_string_free(parts->session_name, TRUE);
620 parts->session_name = NULL;
621 }
622
623end:
624 return;
625}
626
627BT_HIDDEN
628struct bt_common_lttng_live_url_parts bt_common_parse_lttng_live_url(
629 const char *url, char *error_buf, size_t error_buf_size)
630{
631 struct bt_common_lttng_live_url_parts parts;
632 const char *at = url;
633 size_t end_pos;
634
f6ccaed9 635 BT_ASSERT(url);
db0f160a
PP
636 memset(&parts, 0, sizeof(parts));
637 parts.port = -1;
638
639 /* Protocol */
640 parts.proto = bt_common_string_until(at, "", ":", &end_pos);
641 if (!parts.proto || parts.proto->len == 0) {
642 if (error_buf) {
643 snprintf(error_buf, error_buf_size, "Missing protocol");
644 }
645
646 goto error;
647 }
648
649 if (strcmp(parts.proto->str, "net") == 0) {
650 g_string_assign(parts.proto, "net4");
651 }
652
653 if (strcmp(parts.proto->str, "net4") != 0 &&
654 strcmp(parts.proto->str, "net6") != 0) {
655 if (error_buf) {
656 snprintf(error_buf, error_buf_size,
657 "Unknown protocol: `%s`", parts.proto->str);
658 }
659
660 goto error;
661 }
662
663 if (at[end_pos] != ':') {
664 if (error_buf) {
665 snprintf(error_buf, error_buf_size,
666 "Expecting `:` after `%s`", parts.proto->str);
667 }
668
669 goto error;
670 }
671
672 at += end_pos;
673
674 /* :// */
675 if (strncmp(at, "://", 3) != 0) {
676 if (error_buf) {
677 snprintf(error_buf, error_buf_size,
678 "Expecting `://` after protocol");
679 }
680
681 goto error;
682 }
683
684 at += 3;
685
686 /* Hostname */
687 parts.hostname = bt_common_string_until(at, "", ":/", &end_pos);
688 if (!parts.hostname || parts.hostname->len == 0) {
689 if (error_buf) {
690 snprintf(error_buf, error_buf_size, "Missing hostname");
691 }
692
693 goto error;
694 }
695
696 if (at[end_pos] == ':') {
697 /* Port */
698 GString *port;
699
700 at += end_pos + 1;
701 port = bt_common_string_until(at, "", "/", &end_pos);
702 if (!port || port->len == 0) {
703 if (error_buf) {
704 snprintf(error_buf, error_buf_size, "Missing port");
705 }
706
707 goto error;
708 }
709
710 if (sscanf(port->str, "%d", &parts.port) != 1) {
711 if (error_buf) {
712 snprintf(error_buf, error_buf_size,
713 "Invalid port: `%s`", port->str);
714 }
715
716 g_string_free(port, TRUE);
717 goto error;
718 }
719
720 g_string_free(port, TRUE);
721
722 if (parts.port < 0 || parts.port >= 65536) {
723 if (error_buf) {
724 snprintf(error_buf, error_buf_size,
725 "Invalid port: %d", parts.port);
726 }
727
728 goto error;
729 }
730 }
731
732 if (at[end_pos] == '\0') {
733 goto end;
734 }
735
736 at += end_pos;
737
738 /* /host/ */
739 if (strncmp(at, "/host/", 6) != 0) {
740 if (error_buf) {
741 snprintf(error_buf, error_buf_size,
742 "Expecting `/host/` after hostname or port");
743 }
744
745 goto error;
746 }
747
748 at += 6;
749
750 /* Target hostname */
751 parts.target_hostname = bt_common_string_until(at, "", "/", &end_pos);
752 if (!parts.target_hostname || parts.target_hostname->len == 0) {
753 if (error_buf) {
754 snprintf(error_buf, error_buf_size,
755 "Missing target hostname");
756 }
757
758 goto error;
759 }
760
761 if (at[end_pos] == '\0') {
762 goto end;
763 }
764
765 at += end_pos + 1;
766
767 /* Session name */
768 parts.session_name = bt_common_string_until(at, "", "/", &end_pos);
769 if (!parts.session_name || parts.session_name->len == 0) {
770 if (error_buf) {
771 snprintf(error_buf, error_buf_size,
772 "Missing session name");
773 }
774
775 goto error;
776 }
777
778 if (at[end_pos] == '/') {
779 if (error_buf) {
780 snprintf(error_buf, error_buf_size,
781 "Unexpected `/` after session name (`%s`)",
782 parts.session_name->str);
783 }
784
785 goto error;
786 }
787
788 goto end;
789
790error:
791 bt_common_destroy_lttng_live_url_parts(&parts);
792
793end:
794 return parts;
795}
9009cc24
PP
796
797BT_HIDDEN
798void bt_common_normalize_star_glob_pattern(char *pattern)
799{
800 const char *p;
801 char *np;
802 bool got_star = false;
803
f6ccaed9 804 BT_ASSERT(pattern);
9009cc24
PP
805
806 for (p = pattern, np = pattern; *p != '\0'; p++) {
807 switch (*p) {
808 case '*':
809 if (got_star) {
810 /* Avoid consecutive stars. */
811 continue;
812 }
813
814 got_star = true;
815 break;
816 case '\\':
817 /* Copy backslash character. */
818 *np = *p;
819 np++;
820 p++;
821
822 if (*p == '\0') {
823 goto end;
824 }
825
2f69c7e7 826 /* fall-through */
9009cc24
PP
827 default:
828 got_star = false;
829 break;
830 }
831
832 /* Copy single character. */
833 *np = *p;
834 np++;
835 }
836
837end:
838 *np = '\0';
839}
840
841static inline
842bool at_end_of_pattern(const char *p, const char *pattern, size_t pattern_len)
843{
844 return (p - pattern) == pattern_len || *p == '\0';
845}
846
847/*
848 * Globbing matching function with the star feature only (`?` and
849 * character sets are not supported). This matches `candidate` (plain
850 * string) against `pattern`. A literal star can be escaped with `\` in
851 * `pattern`.
852 *
853 * `pattern_len` or `candidate_len` can be greater than the actual
854 * string length of `pattern` or `candidate` if the string is
855 * null-terminated.
856 */
857BT_HIDDEN
858bool bt_common_star_glob_match(const char *pattern, size_t pattern_len,
859 const char *candidate, size_t candidate_len) {
860 const char *retry_c = candidate, *retry_p = pattern, *c, *p;
861 bool got_a_star = false;
862
863retry:
864 c = retry_c;
865 p = retry_p;
866
867 /*
868 * The concept here is to retry a match in the specific case
869 * where we already got a star. The retry position for the
870 * pattern is just after the most recent star, and the retry
871 * position for the candidate is the character following the
872 * last try's first character.
873 *
874 * Example:
875 *
876 * candidate: hi ev every onyx one
877 * ^
878 * pattern: hi*every*one
879 * ^
880 *
881 * candidate: hi ev every onyx one
882 * ^
883 * pattern: hi*every*one
884 * ^
885 *
886 * candidate: hi ev every onyx one
887 * ^
888 * pattern: hi*every*one
889 * ^
890 *
891 * candidate: hi ev every onyx one
892 * ^
893 * pattern: hi*every*one
894 * ^ MISMATCH
895 *
896 * candidate: hi ev every onyx one
897 * ^
898 * pattern: hi*every*one
899 * ^
900 *
901 * candidate: hi ev every onyx one
902 * ^^
903 * pattern: hi*every*one
904 * ^^
905 *
906 * candidate: hi ev every onyx one
907 * ^ ^
908 * pattern: hi*every*one
909 * ^ ^ MISMATCH
910 *
911 * candidate: hi ev every onyx one
912 * ^
913 * pattern: hi*every*one
914 * ^ MISMATCH
915 *
916 * candidate: hi ev every onyx one
917 * ^
918 * pattern: hi*every*one
919 * ^ MISMATCH
920 *
921 * candidate: hi ev every onyx one
922 * ^
923 * pattern: hi*every*one
924 * ^
925 *
926 * candidate: hi ev every onyx one
927 * ^^
928 * pattern: hi*every*one
929 * ^^
930 *
931 * candidate: hi ev every onyx one
932 * ^ ^
933 * pattern: hi*every*one
934 * ^ ^
935 *
936 * candidate: hi ev every onyx one
937 * ^ ^
938 * pattern: hi*every*one
939 * ^ ^
940 *
941 * candidate: hi ev every onyx one
942 * ^ ^
943 * pattern: hi*every*one
944 * ^ ^
945 *
946 * candidate: hi ev every onyx one
947 * ^
948 * pattern: hi*every*one
949 * ^
950 *
951 * candidate: hi ev every onyx one
952 * ^
953 * pattern: hi*every*one
954 * ^ MISMATCH
955 *
956 * candidate: hi ev every onyx one
957 * ^
958 * pattern: hi*every*one
959 * ^
960 *
961 * candidate: hi ev every onyx one
962 * ^^
963 * pattern: hi*every*one
964 * ^^
965 *
966 * candidate: hi ev every onyx one
967 * ^ ^
968 * pattern: hi*every*one
969 * ^ ^ MISMATCH
970 *
971 * candidate: hi ev every onyx one
972 * ^
973 * pattern: hi*every*one
974 * ^ MISMATCH
975 *
976 * candidate: hi ev every onyx one
977 * ^
978 * pattern: hi*every*one
979 * ^ MISMATCH
980 *
981 * candidate: hi ev every onyx one
982 * ^
983 * pattern: hi*every*one
984 * ^ MISMATCH
985 *
986 * candidate: hi ev every onyx one
987 * ^
988 * pattern: hi*every*one
989 * ^ MISMATCH
990 *
991 * candidate: hi ev every onyx one
992 * ^
993 * pattern: hi*every*one
994 * ^
995 *
996 * candidate: hi ev every onyx one
997 * ^^
998 * pattern: hi*every*one
999 * ^^
1000 *
1001 * candidate: hi ev every onyx one
1002 * ^ ^
1003 * pattern: hi*every*one
1004 * ^ ^
1005 *
1006 * candidate: hi ev every onyx one
1007 * ^ ^
1008 * pattern: hi*every*one
1009 * ^ ^ SUCCESS
1010 */
1011 while ((c - candidate) < candidate_len && *c != '\0') {
f6ccaed9 1012 BT_ASSERT(*c);
9009cc24
PP
1013
1014 if (at_end_of_pattern(p, pattern, pattern_len)) {
1015 goto end_of_pattern;
1016 }
1017
1018 switch (*p) {
1019 case '*':
1020 got_a_star = true;
1021
1022 /*
1023 * Our first try starts at the current candidate
1024 * character and after the star in the pattern.
1025 */
1026 retry_c = c;
1027 retry_p = p + 1;
1028
1029 if (at_end_of_pattern(retry_p, pattern, pattern_len)) {
1030 /*
1031 * Star at the end of the pattern at
1032 * this point: automatic match.
1033 */
1034 return true;
1035 }
1036
1037 goto retry;
1038 case '\\':
1039 /* Go to escaped character. */
1040 p++;
1041
1042 /*
1043 * Fall through the default case which compares
1044 * the escaped character now.
1045 */
2f69c7e7 1046 /* fall-through */
9009cc24
PP
1047 default:
1048 if (at_end_of_pattern(p, pattern, pattern_len) ||
1049 *c != *p) {
1050end_of_pattern:
1051 /* Character mismatch OR end of pattern. */
1052 if (!got_a_star) {
1053 /*
1054 * We didn't get any star yet,
1055 * so this first mismatch
1056 * automatically makes the whole
1057 * test fail.
1058 */
1059 return false;
1060 }
1061
1062 /*
1063 * Next try: next candidate character,
1064 * original pattern character (following
1065 * the most recent star).
1066 */
1067 retry_c++;
1068 goto retry;
1069 }
1070 break;
1071 }
1072
1073 /* Next pattern and candidate characters. */
1074 c++;
1075 p++;
1076 }
1077
1078 /*
1079 * We checked every candidate character and we're still in a
1080 * success state: the only pattern character allowed to remain
1081 * is a star.
1082 */
1083 if (at_end_of_pattern(p, pattern, pattern_len)) {
1084 return true;
1085 }
1086
1087 p++;
1088 return p[-1] == '*' && at_end_of_pattern(p, pattern, pattern_len);
1089}
e49a18d1 1090
3a5e626b
MJ
1091#ifdef __MINGW32__
1092BT_HIDDEN
1093GString *bt_common_normalize_path(const char *path, const char *wd)
1094{
1095 char *tmp;
1096 GString *norm_path = NULL;
1097
1098 BT_ASSERT(path);
1099
1100 tmp = _fullpath(NULL, path, PATH_MAX);
1101 if (!tmp) {
1102 goto error;
1103 }
1104
1105 norm_path = g_string_new(tmp);
1106 if (!norm_path) {
1107 goto error;
1108 }
1109
1110 goto end;
1111error:
1112 if (norm_path) {
1113 g_string_free(norm_path, TRUE);
1114 norm_path = NULL;
1115 }
1116end:
1117 if (tmp) {
1118 free(tmp);
1119 }
1120 return norm_path;
1121}
1122#else
e49a18d1
PP
1123static
1124void append_path_parts(const char *path, GPtrArray *parts)
1125{
1126 const char *ch = path;
1127 const char *last = path;
1128
1129 while (true) {
1130 if (*ch == G_DIR_SEPARATOR || *ch == '\0') {
1131 if (ch - last > 0) {
1132 GString *part = g_string_new(NULL);
1133
f6ccaed9 1134 BT_ASSERT(part);
e49a18d1
PP
1135 g_string_append_len(part, last, ch - last);
1136 g_ptr_array_add(parts, part);
1137 }
1138
1139 if (*ch == '\0') {
1140 break;
1141 }
1142
1143 last = ch + 1;
1144 }
1145
1146 ch++;
1147 }
1148}
1149
1150static
1151void destroy_gstring(void *gstring)
1152{
1153 (void) g_string_free(gstring, TRUE);
1154}
1155
1156BT_HIDDEN
1157GString *bt_common_normalize_path(const char *path, const char *wd)
1158{
1159 size_t i;
1160 GString *norm_path;
1161 GPtrArray *parts = NULL;
1162
f6ccaed9 1163 BT_ASSERT(path);
e49a18d1
PP
1164 norm_path = g_string_new(G_DIR_SEPARATOR_S);
1165 if (!norm_path) {
1166 goto error;
1167 }
1168
1169 parts = g_ptr_array_new_with_free_func(destroy_gstring);
1170 if (!parts) {
1171 goto error;
1172 }
1173
1174 if (path[0] != G_DIR_SEPARATOR) {
1175 /* Relative path: start with working directory */
1176 if (wd) {
1177 append_path_parts(wd, parts);
1178 } else {
1179 gchar *cd = g_get_current_dir();
1180
1181 append_path_parts(cd, parts);
1182 g_free(cd);
1183 }
1184 }
1185
1186 /* Append parts of the path parameter */
1187 append_path_parts(path, parts);
1188
1189 /* Resolve special `..` and `.` parts */
1190 for (i = 0; i < parts->len; i++) {
1191 GString *part = g_ptr_array_index(parts, i);
1192
1193 if (strcmp(part->str, "..") == 0) {
1194 if (i == 0) {
1195 /*
1196 * First part of absolute path is `..`:
1197 * this is invalid.
1198 */
1199 goto error;
1200 }
1201
1202 /* Remove `..` and previous part */
1203 g_ptr_array_remove_index(parts, i - 1);
1204 g_ptr_array_remove_index(parts, i - 1);
1205 i -= 2;
1206 } else if (strcmp(part->str, ".") == 0) {
1207 /* Remove `.` */
1208 g_ptr_array_remove_index(parts, i);
1209 i -= 1;
1210 }
1211 }
1212
1213 /* Create normalized path with what's left */
1214 for (i = 0; i < parts->len; i++) {
1215 GString *part = g_ptr_array_index(parts, i);
1216
1217 g_string_append(norm_path, part->str);
1218
1219 if (i < parts->len - 1) {
1220 g_string_append_c(norm_path, G_DIR_SEPARATOR);
1221 }
1222 }
1223
1224 goto end;
1225
1226error:
1227 if (norm_path) {
1228 g_string_free(norm_path, TRUE);
1229 norm_path = NULL;
1230 }
1231
1232end:
1233 if (parts) {
1234 g_ptr_array_free(parts, TRUE);
1235 }
1236
1237 return norm_path;
1238}
1cd3decc 1239#endif
108e5a1e
MJ
1240
1241BT_HIDDEN
86d8b7b8 1242size_t bt_common_get_page_size(int log_level)
108e5a1e
MJ
1243{
1244 int page_size;
1245
1246 page_size = bt_sysconf(_SC_PAGESIZE);
1247 if (page_size < 0) {
b4565e8b
PP
1248 BT_LOGF("Cannot get system's page size: ret=%d",
1249 page_size);
108e5a1e
MJ
1250 abort();
1251 }
1252
1253 return page_size;
1254}
85cd02cf
PP
1255
1256#define BUF_STD_APPEND(...) \
1257 do { \
1258 char _tmp_fmt[64]; \
1259 int _count; \
1260 size_t _size = buf_size - (size_t) (*buf_ch - buf); \
1261 size_t _tmp_fmt_size = (size_t) (fmt_ch - *out_fmt_ch); \
1262 strncpy(_tmp_fmt, *out_fmt_ch, _tmp_fmt_size); \
1263 _tmp_fmt[_tmp_fmt_size] = '\0'; \
1264 _count = snprintf(*buf_ch, _size, _tmp_fmt, __VA_ARGS__); \
f6ccaed9 1265 BT_ASSERT(_count >= 0); \
85cd02cf
PP
1266 *buf_ch += MIN(_count, _size); \
1267 } while (0)
1268
1269#define BUF_STD_APPEND_SINGLE_ARG(_type) \
1270 do { \
1271 _type _arg = va_arg(*args, _type); \
1272 BUF_STD_APPEND(_arg); \
1273 } while (0)
1274
1275static inline void handle_conversion_specifier_std(char *buf, char **buf_ch,
1276 size_t buf_size, const char **out_fmt_ch, va_list *args)
1277{
1278 const char *fmt_ch = *out_fmt_ch;
1279 enum LENGTH_MODIFIER {
1280 LENGTH_MOD_H,
1281 LENGTH_MOD_HH,
1282 LENGTH_MOD_NONE,
1283 LENGTH_MOD_LOW_L,
1284 LENGTH_MOD_LOW_LL,
1285 LENGTH_MOD_UP_L,
1286 LENGTH_MOD_Z,
1287 } length_mod = LENGTH_MOD_NONE;
1288
1289 /* skip '%' */
1290 fmt_ch++;
1291
1292 if (*fmt_ch == '%') {
1293 fmt_ch++;
1294 **buf_ch = '%';
1295 (*buf_ch)++;
1296 goto update_rw_fmt;
1297 }
1298
1299 /* flags */
1300 for (;;) {
1301 switch (*fmt_ch) {
1302 case '-':
1303 case '+':
1304 case ' ':
1305 case '#':
1306 case '0':
1307 case '\'':
1308 fmt_ch++;
1309 continue;
1310 default:
1311 break;
1312 }
1313 break;
1314 }
1315
1316 /* width */
1317 for (;;) {
1318 if (*fmt_ch < '0' || *fmt_ch > '9') {
1319 break;
1320 }
1321
1322 fmt_ch++;
1323 }
1324
1325 /* precision */
1326 if (*fmt_ch == '.') {
1327 fmt_ch++;
1328
1329 for (;;) {
1330 if (*fmt_ch < '0' || *fmt_ch > '9') {
1331 break;
1332 }
1333
1334 fmt_ch++;
1335 }
1336 }
1337
1338 /* format (PRI*64) */
44c440bc 1339 if (strncmp(fmt_ch, PRId64, sizeof(PRId64) - 1) == 0) {
51f4c026 1340 fmt_ch += sizeof(PRId64) - 1;
85cd02cf
PP
1341 BUF_STD_APPEND_SINGLE_ARG(int64_t);
1342 goto update_rw_fmt;
44c440bc 1343 } else if (strncmp(fmt_ch, PRIu64, sizeof(PRIu64) - 1) == 0) {
51f4c026 1344 fmt_ch += sizeof(PRIu64) - 1;
85cd02cf
PP
1345 BUF_STD_APPEND_SINGLE_ARG(uint64_t);
1346 goto update_rw_fmt;
44c440bc 1347 } else if (strncmp(fmt_ch, PRIx64, sizeof(PRIx64) - 1) == 0) {
51f4c026 1348 fmt_ch += sizeof(PRIx64) - 1;
85cd02cf
PP
1349 BUF_STD_APPEND_SINGLE_ARG(uint64_t);
1350 goto update_rw_fmt;
44c440bc 1351 } else if (strncmp(fmt_ch, PRIX64, sizeof(PRIX64) - 1) == 0) {
51f4c026 1352 fmt_ch += sizeof(PRIX64) - 1;
85cd02cf
PP
1353 BUF_STD_APPEND_SINGLE_ARG(uint64_t);
1354 goto update_rw_fmt;
44c440bc 1355 } else if (strncmp(fmt_ch, PRIo64, sizeof(PRIo64) - 1) == 0) {
51f4c026 1356 fmt_ch += sizeof(PRIo64) - 1;
85cd02cf
PP
1357 BUF_STD_APPEND_SINGLE_ARG(uint64_t);
1358 goto update_rw_fmt;
44c440bc 1359 } else if (strncmp(fmt_ch, PRIi64, sizeof(PRIi64) - 1) == 0) {
51f4c026 1360 fmt_ch += sizeof(PRIi64) - 1;
85cd02cf
PP
1361 BUF_STD_APPEND_SINGLE_ARG(int64_t);
1362 goto update_rw_fmt;
1363 }
1364
1365 // length modifier
1366 switch (*fmt_ch) {
1367 case 'h':
1368 length_mod = LENGTH_MOD_H;
1369 fmt_ch++;
1370
1371 if (*fmt_ch == 'h') {
1372 length_mod = LENGTH_MOD_HH;
1373 fmt_ch++;
1374 break;
1375 }
1376 break;
1377 case 'l':
1378 length_mod = LENGTH_MOD_LOW_L;
1379 fmt_ch++;
1380
1381 if (*fmt_ch == 'l') {
1382 length_mod = LENGTH_MOD_LOW_LL;
1383 fmt_ch++;
1384 break;
1385 }
1386 break;
1387 case 'L':
1388 length_mod = LENGTH_MOD_UP_L;
1389 fmt_ch++;
1390 break;
1391 case 'z':
1392 length_mod = LENGTH_MOD_Z;
1393 fmt_ch++;
1394 break;
1395 default:
1396 break;
1397 }
1398
1399 // format
1400 switch (*fmt_ch) {
1401 case 'c':
1402 {
1403 fmt_ch++;
1404
1405 switch (length_mod) {
1406 case LENGTH_MOD_NONE:
1407 BUF_STD_APPEND_SINGLE_ARG(int);
1408 break;
1409 case LENGTH_MOD_LOW_L:
1410 BUF_STD_APPEND_SINGLE_ARG(wint_t);
1411 break;
1412 default:
1413 abort();
1414 }
1415 break;
1416 }
1417 case 's':
1418 fmt_ch++;
1419
1420 switch (length_mod) {
1421 case LENGTH_MOD_NONE:
1422 BUF_STD_APPEND_SINGLE_ARG(char *);
1423 break;
1424 case LENGTH_MOD_LOW_L:
1425 BUF_STD_APPEND_SINGLE_ARG(wchar_t *);
1426 break;
1427 default:
1428 abort();
1429 }
1430 break;
1431 case 'd':
1432 case 'i':
1433 fmt_ch++;
1434
1435 switch (length_mod) {
1436 case LENGTH_MOD_NONE:
1437 case LENGTH_MOD_H:
1438 case LENGTH_MOD_HH:
1439 BUF_STD_APPEND_SINGLE_ARG(int);
1440 break;
1441 case LENGTH_MOD_LOW_L:
1442 BUF_STD_APPEND_SINGLE_ARG(long);
1443 break;
1444 case LENGTH_MOD_LOW_LL:
1445 BUF_STD_APPEND_SINGLE_ARG(long long);
1446 break;
1447 case LENGTH_MOD_Z:
1448 BUF_STD_APPEND_SINGLE_ARG(size_t);
1449 break;
1450 default:
1451 abort();
1452 }
1453 break;
1454 case 'o':
1455 case 'x':
1456 case 'X':
1457 case 'u':
1458 fmt_ch++;
1459
1460 switch (length_mod) {
1461 case LENGTH_MOD_NONE:
1462 case LENGTH_MOD_H:
1463 case LENGTH_MOD_HH:
1464 BUF_STD_APPEND_SINGLE_ARG(unsigned int);
1465 break;
1466 case LENGTH_MOD_LOW_L:
1467 BUF_STD_APPEND_SINGLE_ARG(unsigned long);
1468 break;
1469 case LENGTH_MOD_LOW_LL:
1470 BUF_STD_APPEND_SINGLE_ARG(unsigned long long);
1471 break;
1472 case LENGTH_MOD_Z:
1473 BUF_STD_APPEND_SINGLE_ARG(size_t);
1474 break;
1475 default:
1476 abort();
1477 }
1478 break;
1479 case 'f':
1480 case 'F':
1481 case 'e':
1482 case 'E':
1483 case 'g':
1484 case 'G':
1485 fmt_ch++;
1486
1487 switch (length_mod) {
1488 case LENGTH_MOD_NONE:
1489 BUF_STD_APPEND_SINGLE_ARG(double);
1490 break;
1491 case LENGTH_MOD_UP_L:
1492 BUF_STD_APPEND_SINGLE_ARG(long double);
1493 break;
1494 default:
1495 abort();
1496 }
1497 break;
1498 case 'p':
1499 fmt_ch++;
1500
1501 if (length_mod == LENGTH_MOD_NONE) {
1502 BUF_STD_APPEND_SINGLE_ARG(void *);
1503 } else {
1504 abort();
1505 }
1506 break;
1507 default:
1508 abort();
1509 }
1510
1511update_rw_fmt:
1512 *out_fmt_ch = fmt_ch;
1513}
1514
1515BT_HIDDEN
1516void bt_common_custom_vsnprintf(char *buf, size_t buf_size,
1517 char intro,
1518 bt_common_handle_custom_specifier_func handle_specifier,
1519 void *priv_data, const char *fmt, va_list *args)
1520{
1521 const char *fmt_ch = fmt;
1522 char *buf_ch = buf;
1523
f6ccaed9
PP
1524 BT_ASSERT(buf);
1525 BT_ASSERT(fmt);
85cd02cf
PP
1526
1527 while (*fmt_ch != '\0') {
1528 switch (*fmt_ch) {
1529 case '%':
f6ccaed9 1530 BT_ASSERT(fmt_ch[1] != '\0');
85cd02cf
PP
1531
1532 if (fmt_ch[1] == intro) {
1533 handle_specifier(priv_data, &buf_ch,
1534 buf_size - (size_t) (buf_ch - buf),
1535 &fmt_ch, args);
1536 } else {
1537 handle_conversion_specifier_std(buf, &buf_ch,
1538 buf_size, &fmt_ch, args);
1539 }
1540
1541 if (buf_ch >= buf + buf_size - 1) {
1542 fmt_ch = "";
1543 }
1544 break;
1545 default:
1546 *buf_ch = *fmt_ch;
1547 buf_ch++;
1548 if (buf_ch >= buf + buf_size - 1) {
1549 fmt_ch = "";
1550 }
1551
1552 fmt_ch++;
1553 }
1554 }
1555
1556 *buf_ch = '\0';
1557}
1558
1559BT_HIDDEN
1560void bt_common_custom_snprintf(char *buf, size_t buf_size,
1561 char intro,
1562 bt_common_handle_custom_specifier_func handle_specifier,
1563 void *priv_data, const char *fmt, ...)
1564{
1565 va_list args;
1566 va_start(args, fmt);
1567 bt_common_custom_vsnprintf(buf, buf_size, intro, handle_specifier,
1568 priv_data, fmt, &args);
1569 va_end(args);
1570}
ae83436e
PP
1571
1572BT_HIDDEN
1573void bt_common_sep_digits(char *str, unsigned int digits_per_group, char sep)
1574{
1575 const char *rd;
1576 char *wr;
1577 uint64_t i = 0;
1578 uint64_t orig_len;
1579 uint64_t sep_count;
1580 uint64_t new_len;
1581
1582 BT_ASSERT(digits_per_group > 0);
1583 BT_ASSERT(sep != '\0');
1584
1585 /* Compute new length of `str` */
1586 orig_len = strlen(str);
1587 BT_ASSERT(orig_len > 0);
1588 sep_count = (orig_len - 1) / digits_per_group;
1589 new_len = strlen(str) + sep_count;
1590
1591 /*
1592 * Do the work in place. Have the reading pointer `rd` start at
1593 * the end of the original string, and the writing pointer `wr`
1594 * start at the end of the new string, making sure to also put a
1595 * null character there.
1596 */
1597 rd = str + orig_len - 1;
1598 wr = str + new_len;
1599 *wr = '\0';
1600 wr--;
1601
1602 /*
1603 * Here's what the process looks like (3 digits per group):
1604 *
1605 * Source: 12345678
1606 * ^
1607 * Destination: 12345678#8
1608 * ^
1609 *
1610 * Source: 12345678
1611 * ^
1612 * Destination: 1234567878
1613 * ^
1614 *
1615 * Source: 12345678
1616 * ^
1617 * Destination: 1234567678
1618 * ^
1619 *
1620 * Source: 12345678
1621 * ^
1622 * Destination: 123456,678
1623 * ^
1624 *
1625 * Source: 12345678
1626 * ^
1627 * Destination: 123455,678
1628 * ^
1629 *
1630 * Source: 12345678
1631 * ^
1632 * Destination: 123445,678
1633 * ^
1634 *
1635 * Source: 12345678
1636 * ^
1637 * Destination: 123345,678
1638 * ^
1639 *
1640 * Source: 12345678
1641 * ^
1642 * Destination: 12,345,678
1643 * ^
1644 *
1645 * Source: 12345678
1646 * ^
1647 * Destination: 12,345,678
1648 * ^
1649 *
1650 * Source: 12345678
1651 * ^
1652 * Destination: 12,345,678
1653 * ^
1654 */
1655 while (rd != str - 1) {
1656 if (i == digits_per_group) {
1657 /*
1658 * Time to append the separator: decrement `wr`,
1659 * but keep `rd` as is.
1660 */
1661 i = 0;
1662 *wr = sep;
1663 wr--;
1664 continue;
1665 }
1666
1667 /* Copy read-side character to write-side character */
1668 *wr = *rd;
1669 wr--;
1670 rd--;
1671 i++;
1672 }
1673}
This page took 0.107171 seconds and 4 git commands to generate.