1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25#include "qemu/osdep.h"
26#include "qemu/host-utils.h"
27#include <math.h>
28
29#ifdef __FreeBSD__
30#include <sys/sysctl.h>
31#include <sys/user.h>
32#endif
33
34#ifdef __NetBSD__
35#include <sys/sysctl.h>
36#endif
37
38#ifdef __HAIKU__
39#include <kernel/image.h>
40#endif
41
42#ifdef __APPLE__
43#include <mach-o/dyld.h>
44#endif
45
46#ifdef G_OS_WIN32
47#include <pathcch.h>
48#include <wchar.h>
49#endif
50
51#include "qemu/ctype.h"
52#include "qemu/cutils.h"
53#include "qemu/error-report.h"
54
55void strpadcpy(char *buf, int buf_size, const char *str, char pad)
56{
57 int len = qemu_strnlen(str, buf_size);
58 memcpy(buf, str, len);
59 memset(buf + len, pad, buf_size - len);
60}
61
62void pstrcpy(char *buf, int buf_size, const char *str)
63{
64 int c;
65 char *q = buf;
66
67 if (buf_size <= 0)
68 return;
69
70 for(;;) {
71 c = *str++;
72 if (c == 0 || q >= buf + buf_size - 1)
73 break;
74 *q++ = c;
75 }
76 *q = '\0';
77}
78
79
80char *pstrcat(char *buf, int buf_size, const char *s)
81{
82 int len;
83 len = strlen(buf);
84 if (len < buf_size)
85 pstrcpy(buf + len, buf_size - len, s);
86 return buf;
87}
88
89int strstart(const char *str, const char *val, const char **ptr)
90{
91 const char *p, *q;
92 p = str;
93 q = val;
94 while (*q != '\0') {
95 if (*p != *q)
96 return 0;
97 p++;
98 q++;
99 }
100 if (ptr)
101 *ptr = p;
102 return 1;
103}
104
105int stristart(const char *str, const char *val, const char **ptr)
106{
107 const char *p, *q;
108 p = str;
109 q = val;
110 while (*q != '\0') {
111 if (qemu_toupper(*p) != qemu_toupper(*q))
112 return 0;
113 p++;
114 q++;
115 }
116 if (ptr)
117 *ptr = p;
118 return 1;
119}
120
121
122int qemu_strnlen(const char *s, int max_len)
123{
124 int i;
125
126 for(i = 0; i < max_len; i++) {
127 if (s[i] == '\0') {
128 break;
129 }
130 }
131 return i;
132}
133
134char *qemu_strsep(char **input, const char *delim)
135{
136 char *result = *input;
137 if (result != NULL) {
138 char *p;
139
140 for (p = result; *p != '\0'; p++) {
141 if (strchr(delim, *p)) {
142 break;
143 }
144 }
145 if (*p == '\0') {
146 *input = NULL;
147 } else {
148 *p = '\0';
149 *input = p + 1;
150 }
151 }
152 return result;
153}
154
155time_t mktimegm(struct tm *tm)
156{
157 time_t t;
158 int y = tm->tm_year + 1900, m = tm->tm_mon + 1, d = tm->tm_mday;
159 if (m < 3) {
160 m += 12;
161 y--;
162 }
163 t = 86400ULL * (d + (153 * m - 457) / 5 + 365 * y + y / 4 - y / 100 +
164 y / 400 - 719469);
165 t += 3600 * tm->tm_hour + 60 * tm->tm_min + tm->tm_sec;
166 return t;
167}
168
169static int64_t suffix_mul(char suffix, int64_t unit)
170{
171 switch (qemu_toupper(suffix)) {
172 case 'B':
173 return 1;
174 case 'K':
175 return unit;
176 case 'M':
177 return unit * unit;
178 case 'G':
179 return unit * unit * unit;
180 case 'T':
181 return unit * unit * unit * unit;
182 case 'P':
183 return unit * unit * unit * unit * unit;
184 case 'E':
185 return unit * unit * unit * unit * unit * unit;
186 }
187 return -1;
188}
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223static int do_strtosz(const char *nptr, const char **end,
224 const char default_suffix, int64_t unit,
225 uint64_t *result)
226{
227 int retval;
228 const char *endptr;
229 unsigned char c;
230 uint64_t val = 0, valf = 0;
231 int64_t mul;
232
233
234 retval = parse_uint(nptr, &endptr, 10, &val);
235 if (retval == -ERANGE || !nptr) {
236 goto out;
237 }
238 if (retval == 0 && val == 0 && (*endptr == 'x' || *endptr == 'X')) {
239
240 retval = qemu_strtou64(nptr, &endptr, 16, &val);
241 if (retval) {
242 goto out;
243 }
244 if (*endptr == '.' || suffix_mul(*endptr, unit) > 0) {
245 endptr = nptr;
246 retval = -EINVAL;
247 goto out;
248 }
249 } else if (*endptr == '.' || (endptr == nptr && strchr(nptr, '.'))) {
250
251
252
253
254
255
256 double fraction = 0.0;
257
258 if (retval == 0 && *endptr == '.' && !isdigit(endptr[1])) {
259
260 endptr++;
261 } else {
262 char *e;
263 const char *tail;
264 g_autofree char *copy = g_strdup(endptr);
265
266 e = strchr(copy, 'e');
267 if (e) {
268 *e = '\0';
269 }
270 e = strchr(copy, 'E');
271 if (e) {
272 *e = '\0';
273 }
274
275
276
277
278
279
280
281
282 retval = qemu_strtod_finite(copy, &tail, &fraction);
283 endptr += tail - copy;
284 if (signbit(fraction)) {
285 retval = -ERANGE;
286 goto out;
287 }
288 }
289
290
291 if (fraction == 1.0) {
292 if (val == UINT64_MAX) {
293 retval = -ERANGE;
294 goto out;
295 }
296 val++;
297 } else if (retval == -ERANGE) {
298
299 valf = 1;
300 retval = 0;
301 } else {
302
303 valf = (uint64_t)(fraction * 0x1p64);
304 if (valf == 0 && fraction > 0.0) {
305 valf = 1;
306 }
307 }
308 }
309 if (retval) {
310 goto out;
311 }
312 c = *endptr;
313 mul = suffix_mul(c, unit);
314 if (mul > 0) {
315 endptr++;
316 } else {
317 mul = suffix_mul(default_suffix, unit);
318 assert(mul > 0);
319 }
320 if (mul == 1) {
321
322 if (valf != 0) {
323 endptr = nptr;
324 retval = -EINVAL;
325 goto out;
326 }
327 } else {
328 uint64_t valh, tmp;
329
330
331 mulu64(&val, &valh, val, mul);
332 mulu64(&valf, &tmp, valf, mul);
333 val += tmp;
334 valh += val < tmp;
335
336
337 tmp = valf >> 63;
338 val += tmp;
339 valh += val < tmp;
340
341
342 if (valh != 0) {
343 retval = -ERANGE;
344 goto out;
345 }
346 }
347
348 retval = 0;
349
350out:
351 if (end) {
352 *end = endptr;
353 } else if (nptr && *endptr) {
354 retval = -EINVAL;
355 }
356 if (retval == 0) {
357 *result = val;
358 } else {
359 *result = 0;
360 if (end && retval == -EINVAL) {
361 *end = nptr;
362 }
363 }
364
365 return retval;
366}
367
368int qemu_strtosz(const char *nptr, const char **end, uint64_t *result)
369{
370 return do_strtosz(nptr, end, 'B', 1024, result);
371}
372
373int qemu_strtosz_MiB(const char *nptr, const char **end, uint64_t *result)
374{
375 return do_strtosz(nptr, end, 'M', 1024, result);
376}
377
378int qemu_strtosz_metric(const char *nptr, const char **end, uint64_t *result)
379{
380 return do_strtosz(nptr, end, 'B', 1000, result);
381}
382
383
384
385
386static int check_strtox_error(const char *nptr, char *ep,
387 const char **endptr, bool check_zero,
388 int libc_errno)
389{
390 assert(ep >= nptr);
391
392
393 if (check_zero && ep == nptr && libc_errno == 0) {
394 char *tmp;
395
396 errno = 0;
397 if (strtol(nptr, &tmp, 10) == 0 && errno == 0 &&
398 (*tmp == 'x' || *tmp == 'X')) {
399 ep = tmp;
400 }
401 }
402
403 if (endptr) {
404 *endptr = ep;
405 }
406
407
408 if (libc_errno == 0 && ep == nptr) {
409 return -EINVAL;
410 }
411
412
413 if (!endptr && *ep) {
414 return -EINVAL;
415 }
416
417 return -libc_errno;
418}
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448int qemu_strtoi(const char *nptr, const char **endptr, int base,
449 int *result)
450{
451 char *ep;
452 long long lresult;
453
454 assert((unsigned) base <= 36 && base != 1);
455 if (!nptr) {
456 *result = 0;
457 if (endptr) {
458 *endptr = nptr;
459 }
460 return -EINVAL;
461 }
462
463 errno = 0;
464 lresult = strtoll(nptr, &ep, base);
465 if (lresult < INT_MIN) {
466 *result = INT_MIN;
467 errno = ERANGE;
468 } else if (lresult > INT_MAX) {
469 *result = INT_MAX;
470 errno = ERANGE;
471 } else {
472 *result = lresult;
473 }
474 return check_strtox_error(nptr, ep, endptr, lresult == 0, errno);
475}
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504int qemu_strtoui(const char *nptr, const char **endptr, int base,
505 unsigned int *result)
506{
507 char *ep;
508 unsigned long long lresult;
509 bool neg;
510
511 assert((unsigned) base <= 36 && base != 1);
512 if (!nptr) {
513 *result = 0;
514 if (endptr) {
515 *endptr = nptr;
516 }
517 return -EINVAL;
518 }
519
520 errno = 0;
521 lresult = strtoull(nptr, &ep, base);
522
523
524 if (errno == ERANGE) {
525 *result = -1;
526 } else {
527
528
529
530
531
532
533
534 neg = memchr(nptr, '-', ep - nptr) != NULL;
535 if (neg) {
536 lresult = -lresult;
537 }
538 if (lresult > UINT_MAX) {
539 *result = UINT_MAX;
540 errno = ERANGE;
541 } else {
542 *result = neg ? -lresult : lresult;
543 }
544 }
545 return check_strtox_error(nptr, ep, endptr, lresult == 0, errno);
546}
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573int qemu_strtol(const char *nptr, const char **endptr, int base,
574 long *result)
575{
576 char *ep;
577
578 assert((unsigned) base <= 36 && base != 1);
579 if (!nptr) {
580 *result = 0;
581 if (endptr) {
582 *endptr = nptr;
583 }
584 return -EINVAL;
585 }
586
587 errno = 0;
588 *result = strtol(nptr, &ep, base);
589 return check_strtox_error(nptr, ep, endptr, *result == 0, errno);
590}
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618int qemu_strtoul(const char *nptr, const char **endptr, int base,
619 unsigned long *result)
620{
621 char *ep;
622
623 assert((unsigned) base <= 36 && base != 1);
624 if (!nptr) {
625 *result = 0;
626 if (endptr) {
627 *endptr = nptr;
628 }
629 return -EINVAL;
630 }
631
632 errno = 0;
633 *result = strtoul(nptr, &ep, base);
634
635 if (errno == ERANGE) {
636 *result = -1;
637 }
638 return check_strtox_error(nptr, ep, endptr, *result == 0, errno);
639}
640
641
642
643
644
645
646
647int qemu_strtoi64(const char *nptr, const char **endptr, int base,
648 int64_t *result)
649{
650 char *ep;
651
652 assert((unsigned) base <= 36 && base != 1);
653 if (!nptr) {
654 *result = 0;
655 if (endptr) {
656 *endptr = nptr;
657 }
658 return -EINVAL;
659 }
660
661
662 QEMU_BUILD_BUG_ON(sizeof(int64_t) != sizeof(long long));
663 errno = 0;
664 *result = strtoll(nptr, &ep, base);
665 return check_strtox_error(nptr, ep, endptr, *result == 0, errno);
666}
667
668
669
670
671
672
673
674
675int qemu_strtou64(const char *nptr, const char **endptr, int base,
676 uint64_t *result)
677{
678 char *ep;
679
680 assert((unsigned) base <= 36 && base != 1);
681 if (!nptr) {
682 *result = 0;
683 if (endptr) {
684 *endptr = nptr;
685 }
686 return -EINVAL;
687 }
688
689
690 QEMU_BUILD_BUG_ON(sizeof(uint64_t) != sizeof(unsigned long long));
691 errno = 0;
692 *result = strtoull(nptr, &ep, base);
693
694 if (errno == ERANGE) {
695 *result = -1;
696 }
697 return check_strtox_error(nptr, ep, endptr, *result == 0, errno);
698}
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725int qemu_strtod(const char *nptr, const char **endptr, double *result)
726{
727 char *ep;
728
729 if (!nptr) {
730 *result = 0.0;
731 if (endptr) {
732 *endptr = nptr;
733 }
734 return -EINVAL;
735 }
736
737 errno = 0;
738 *result = strtod(nptr, &ep);
739 return check_strtox_error(nptr, ep, endptr, false, errno);
740}
741
742
743
744
745
746
747
748
749
750
751int qemu_strtod_finite(const char *nptr, const char **endptr, double *result)
752{
753 const char *tmp;
754 int ret;
755
756 ret = qemu_strtod(nptr, &tmp, result);
757 if (!isfinite(*result)) {
758 if (endptr) {
759 *endptr = nptr;
760 }
761 *result = 0.0;
762 ret = -EINVAL;
763 } else if (endptr) {
764 *endptr = tmp;
765 } else if (*tmp) {
766 ret = -EINVAL;
767 }
768 return ret;
769}
770
771
772
773
774
775#ifndef HAVE_STRCHRNUL
776const char *qemu_strchrnul(const char *s, int c)
777{
778 const char *e = strchr(s, c);
779 if (!e) {
780 e = s + strlen(s);
781 }
782 return e;
783}
784#endif
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816int parse_uint(const char *s, const char **endptr, int base, uint64_t *value)
817{
818 int r = 0;
819 char *endp = (char *)s;
820 unsigned long long val = 0;
821
822 assert((unsigned) base <= 36 && base != 1);
823 if (!s) {
824 r = -EINVAL;
825 goto out;
826 }
827
828 errno = 0;
829 val = strtoull(s, &endp, base);
830 if (errno) {
831 r = -errno;
832 goto out;
833 }
834
835 if (endp == s) {
836 r = -EINVAL;
837 goto out;
838 }
839
840
841 while (qemu_isspace(*s)) {
842 s++;
843 }
844 if (*s == '-') {
845 val = 0;
846 r = -ERANGE;
847 goto out;
848 }
849
850out:
851 *value = val;
852 if (endptr) {
853 *endptr = endp;
854 } else if (s && *endp) {
855 r = -EINVAL;
856 *value = 0;
857 }
858 return r;
859}
860
861
862
863
864
865
866
867
868
869
870
871
872int parse_uint_full(const char *s, int base, uint64_t *value)
873{
874 return parse_uint(s, NULL, base, value);
875}
876
877int qemu_parse_fd(const char *param)
878{
879 long fd;
880 char *endptr;
881
882 errno = 0;
883 fd = strtol(param, &endptr, 10);
884 if (param == endptr ||
885 errno != 0 ||
886 *endptr != '\0' ||
887 fd < 0 ||
888 fd > INT_MAX ) {
889 return -1;
890 }
891 return fd;
892}
893
894
895
896
897
898int uleb128_encode_small(uint8_t *out, uint32_t n)
899{
900 g_assert(n <= 0x3fff);
901 if (n < 0x80) {
902 *out = n;
903 return 1;
904 } else {
905 *out++ = (n & 0x7f) | 0x80;
906 *out = n >> 7;
907 return 2;
908 }
909}
910
911int uleb128_decode_small(const uint8_t *in, uint32_t *n)
912{
913 if (!(*in & 0x80)) {
914 *n = *in;
915 return 1;
916 } else {
917 *n = *in++ & 0x7f;
918
919 if (*in & 0x80) {
920 return -1;
921 }
922 *n |= *in << 7;
923 return 2;
924 }
925}
926
927
928
929
930int parse_debug_env(const char *name, int max, int initial)
931{
932 char *debug_env = getenv(name);
933 char *inv = NULL;
934 long debug;
935
936 if (!debug_env) {
937 return initial;
938 }
939 errno = 0;
940 debug = strtol(debug_env, &inv, 10);
941 if (inv == debug_env) {
942 return initial;
943 }
944 if (debug < 0 || debug > max || errno != 0) {
945 warn_report("%s not in [0, %d]", name, max);
946 return initial;
947 }
948 return debug;
949}
950
951const char *si_prefix(unsigned int exp10)
952{
953 static const char *prefixes[] = {
954 "a", "f", "p", "n", "u", "m", "", "K", "M", "G", "T", "P", "E"
955 };
956
957 exp10 += 18;
958 assert(exp10 % 3 == 0 && exp10 / 3 < ARRAY_SIZE(prefixes));
959 return prefixes[exp10 / 3];
960}
961
962const char *iec_binary_prefix(unsigned int exp2)
963{
964 static const char *prefixes[] = { "", "Ki", "Mi", "Gi", "Ti", "Pi", "Ei" };
965
966 assert(exp2 % 10 == 0 && exp2 / 10 < ARRAY_SIZE(prefixes));
967 return prefixes[exp2 / 10];
968}
969
970
971
972
973
974
975
976char *size_to_str(uint64_t val)
977{
978 uint64_t div;
979 int i;
980
981
982
983
984
985
986
987 frexp(val / (1000.0 / 1024.0), &i);
988 i = (i - 1) / 10 * 10;
989 div = 1ULL << i;
990
991 return g_strdup_printf("%0.3g %sB", (double)val / div, iec_binary_prefix(i));
992}
993
994char *freq_to_str(uint64_t freq_hz)
995{
996 double freq = freq_hz;
997 size_t exp10 = 0;
998
999 while (freq >= 1000.0) {
1000 freq /= 1000.0;
1001 exp10 += 3;
1002 }
1003
1004 return g_strdup_printf("%0.3g %sHz", freq, si_prefix(exp10));
1005}
1006
1007int qemu_pstrcmp0(const char **str1, const char **str2)
1008{
1009 return g_strcmp0(*str1, *str2);
1010}
1011
1012static inline bool starts_with_prefix(const char *dir)
1013{
1014 size_t prefix_len = strlen(CONFIG_PREFIX);
1015
1016
1017
1018
1019#pragma GCC diagnostic push
1020#if !defined(__clang__) || __has_warning("-Warray-bounds=")
1021#pragma GCC diagnostic ignored "-Warray-bounds="
1022#endif
1023 return !memcmp(dir, CONFIG_PREFIX, prefix_len) &&
1024 (!dir[prefix_len] || G_IS_DIR_SEPARATOR(dir[prefix_len]));
1025#pragma GCC diagnostic pop
1026}
1027
1028
1029static inline const char *next_component(const char *dir, int *p_len)
1030{
1031 int len;
1032 while ((*dir && G_IS_DIR_SEPARATOR(*dir)) ||
1033 (*dir == '.' && (G_IS_DIR_SEPARATOR(dir[1]) || dir[1] == '\0'))) {
1034 dir++;
1035 }
1036 len = 0;
1037 while (dir[len] && !G_IS_DIR_SEPARATOR(dir[len])) {
1038 len++;
1039 }
1040 *p_len = len;
1041 return dir;
1042}
1043
1044static const char *exec_dir;
1045
1046void qemu_init_exec_dir(const char *argv0)
1047{
1048#ifdef G_OS_WIN32
1049 char *p;
1050 char buf[MAX_PATH];
1051 DWORD len;
1052
1053 if (exec_dir) {
1054 return;
1055 }
1056
1057 len = GetModuleFileName(NULL, buf, sizeof(buf) - 1);
1058 if (len == 0) {
1059 return;
1060 }
1061
1062 buf[len] = 0;
1063 p = buf + len - 1;
1064 while (p != buf && *p != '\\') {
1065 p--;
1066 }
1067 *p = 0;
1068 if (access(buf, R_OK) == 0) {
1069 exec_dir = g_strdup(buf);
1070 } else {
1071 exec_dir = CONFIG_BINDIR;
1072 }
1073#else
1074 char *p = NULL;
1075 char buf[PATH_MAX];
1076
1077 if (exec_dir) {
1078 return;
1079 }
1080
1081#if defined(__linux__)
1082 {
1083 int len;
1084 len = readlink("/proc/self/exe", buf, sizeof(buf) - 1);
1085 if (len > 0) {
1086 buf[len] = 0;
1087 p = buf;
1088 }
1089 }
1090#elif defined(__FreeBSD__) \
1091 || (defined(__NetBSD__) && defined(KERN_PROC_PATHNAME))
1092 {
1093#if defined(__FreeBSD__)
1094 static int mib[4] = {CTL_KERN, KERN_PROC, KERN_PROC_PATHNAME, -1};
1095#else
1096 static int mib[4] = {CTL_KERN, KERN_PROC_ARGS, -1, KERN_PROC_PATHNAME};
1097#endif
1098 size_t len = sizeof(buf) - 1;
1099
1100 *buf = '\0';
1101 if (!sysctl(mib, ARRAY_SIZE(mib), buf, &len, NULL, 0) &&
1102 *buf) {
1103 buf[sizeof(buf) - 1] = '\0';
1104 p = buf;
1105 }
1106 }
1107#elif defined(__APPLE__)
1108 {
1109 char fpath[PATH_MAX];
1110 uint32_t len = sizeof(fpath);
1111 if (_NSGetExecutablePath(fpath, &len) == 0) {
1112 p = realpath(fpath, buf);
1113 if (!p) {
1114 return;
1115 }
1116 }
1117 }
1118#elif defined(__HAIKU__)
1119 {
1120 image_info ii;
1121 int32_t c = 0;
1122
1123 *buf = '\0';
1124 while (get_next_image_info(0, &c, &ii) == B_OK) {
1125 if (ii.type == B_APP_IMAGE) {
1126 strncpy(buf, ii.name, sizeof(buf));
1127 buf[sizeof(buf) - 1] = 0;
1128 p = buf;
1129 break;
1130 }
1131 }
1132 }
1133#endif
1134
1135
1136 if (!p && argv0) {
1137 p = realpath(argv0, buf);
1138 }
1139 if (p) {
1140 exec_dir = g_path_get_dirname(p);
1141 } else {
1142 exec_dir = CONFIG_BINDIR;
1143 }
1144#endif
1145}
1146
1147char *get_relocated_path(const char *dir)
1148{
1149 size_t prefix_len = strlen(CONFIG_PREFIX);
1150 const char *bindir = CONFIG_BINDIR;
1151 GString *result;
1152 int len_dir, len_bindir;
1153
1154
1155 assert(exec_dir[0]);
1156
1157 result = g_string_new(exec_dir);
1158 g_string_append(result, "/qemu-bundle");
1159 if (access(result->str, R_OK) == 0) {
1160#ifdef G_OS_WIN32
1161 const char *src = dir;
1162 size_t size = mbsrtowcs(NULL, &src, 0, &(mbstate_t){0}) + 1;
1163 PWSTR wdir = g_new(WCHAR, size);
1164 mbsrtowcs(wdir, &src, size, &(mbstate_t){0});
1165
1166 PCWSTR wdir_skipped_root;
1167 if (PathCchSkipRoot(wdir, &wdir_skipped_root) == S_OK) {
1168 size = wcsrtombs(NULL, &wdir_skipped_root, 0, &(mbstate_t){0});
1169 char *cursor = result->str + result->len;
1170 g_string_set_size(result, result->len + size);
1171 wcsrtombs(cursor, &wdir_skipped_root, size + 1, &(mbstate_t){0});
1172 } else {
1173 g_string_append(result, dir);
1174 }
1175
1176 g_free(wdir);
1177#else
1178 g_string_append(result, dir);
1179#endif
1180 goto out;
1181 }
1182
1183 if (IS_ENABLED(CONFIG_RELOCATABLE) &&
1184 starts_with_prefix(dir) && starts_with_prefix(bindir)) {
1185 g_string_assign(result, exec_dir);
1186
1187
1188 len_dir = len_bindir = prefix_len;
1189 do {
1190 dir += len_dir;
1191 bindir += len_bindir;
1192 dir = next_component(dir, &len_dir);
1193 bindir = next_component(bindir, &len_bindir);
1194 } while (len_dir && len_dir == len_bindir && !memcmp(dir, bindir, len_dir));
1195
1196
1197 while (len_bindir) {
1198 bindir += len_bindir;
1199 g_string_append(result, "/..");
1200 bindir = next_component(bindir, &len_bindir);
1201 }
1202
1203 if (*dir) {
1204 assert(G_IS_DIR_SEPARATOR(dir[-1]));
1205 g_string_append(result, dir - 1);
1206 }
1207 goto out;
1208 }
1209
1210 g_string_assign(result, dir);
1211out:
1212 return g_string_free(result, false);
1213}
1214