1 #define _XOPEN_SOURCE 500 /* For strdup(3) */
2 #define _POSIX_C_SOURCE 200112L /* For setenv() */
14 char *tz_orig
; /* Copy of original TZ variable */
16 /* Concatenate two strings into newly allocated buffer.
17 * You must free() them, when you don't need it anymore.
18 * Any of the arguments can be NULL meaning empty string.
19 * In case of error returns NULL.
20 * Empty string is always returned as allocated empty string. */
21 _hidden
char *astrcat(const char *first
, const char *second
) {
22 size_t first_len
, second_len
;
25 first_len
= (first
) ? strlen(first
) : 0;
26 second_len
= (second
) ? strlen(second
) : 0;
27 buf
= malloc(1 + first_len
+ second_len
);
30 if (first
) strcpy(buf
, first
);
31 if (second
) strcpy(buf
+ first_len
, second
);
37 /* Concatenate three strings into newly allocated buffer.
38 * You must free() them, when you don't need it anymore.
39 * Any of the arguments can be NULL meaning empty string.
40 * In case of error returns NULL.
41 * Empty string is always returned as allocated empty string. */
42 _hidden
char *astrcat3(const char *first
, const char *second
,
44 size_t first_len
, second_len
, third_len
;
47 first_len
= (first
) ? strlen(first
) : 0;
48 second_len
= (second
) ? strlen(second
) : 0;
49 third_len
= (third
) ? strlen(third
) : 0;
50 buf
= malloc(1 + first_len
+ second_len
+ third_len
);
70 /* Print formatted string into automatically reallocated @buffer.
71 * @buffer automatically reallocated buffer. Must be &NULL or preallocated
73 * @format format string as for printf(3)
74 * @ap list of variadic arguments, after call will be in undefined state
75 * @Returns number of bytes printed. In case of error, -1 and NULL @buffer*/
76 _hidden
int shi_vasprintf(char **buffer
, const char *format
, va_list ap
) {
78 int length
, new_length
;
81 if (!buffer
|| !format
) {
90 length
= vsnprintf(NULL
, 0, format
, aq
) + 1;
98 new_buffer
= realloc(*buffer
, length
);
104 *buffer
= new_buffer
;
106 new_length
= vsnprintf(*buffer
, length
, format
, ap
);
107 if (new_length
>= length
) {
117 /* Print formatted string into automatically reallocated @buffer.
118 * @buffer automatically reallocated buffer. Must be &NULL or preallocated
120 * @format format string as for printf(3)
121 * @... variadic arguments
122 * @Returns number of bytes printed. In case of error, -1 and NULL @buffer*/
123 _hidden
int shi_asprintf(char **buffer
, const char *format
, ...) {
126 va_start(ap
, format
);
127 ret
= shi_vasprintf(buffer
, format
, ap
);
133 /* Converts UTF8 string into locale encoded string.
134 * @utf string in UTF-8 terminated by zero byte
135 * @return allocated string encoded in locale specific encoding. You must free
136 * it. In case of error or NULL @utf returns NULL. */
137 _hidden
char *utf82locale(const char *utf
) {
140 char *buffer
= NULL
, *new_buffer
;
141 size_t buffer_length
= 0, buffer_used
= 0;
142 char *inbuf
, *outbuf
;
143 size_t inleft
, outleft
;
145 if (!utf
) return NULL
;
147 /* nl_langinfo() is not thread-safe */
148 state
= iconv_open(nl_langinfo(CODESET
), "UTF-8");
149 if (state
== (iconv_t
) -1) return NULL
;
151 /* Get the initial output buffer length */
152 utf_length
= strlen(utf
);
153 buffer_length
= utf_length
+ 1;
155 inbuf
= (char *) utf
;
156 inleft
= utf_length
+ 1;
160 new_buffer
= realloc(buffer
, buffer_length
);
169 outbuf
= buffer
+ buffer_used
;
170 outleft
= buffer_length
- buffer_used
;
172 /* Convert chunk of data */
173 if ((size_t) -1 == iconv(state
, &inbuf
, &inleft
, &outbuf
, &outleft
) &&
180 /* Update positions */
181 buffer_length
+= 1024;
182 buffer_used
= outbuf
- buffer
;
191 /* Converts locale encoded string into UTF8.
192 * @locale string in locale encoded string terminated by zero byte
193 * @return allocated string encoded in UTF-8 encoding. You must free
194 * it. In case of error or NULL @locale returns NULL. */
195 _hidden
char *locale2utf8(const char *locale
) {
197 size_t locale_length
;
198 char *buffer
= NULL
, *new_buffer
;
199 size_t buffer_length
= 0, buffer_used
= 0;
200 char *inbuf
, *outbuf
;
201 size_t inleft
, outleft
;
203 if (!locale
) return NULL
;
205 /* nl_langinfo() is not thread-safe */
206 state
= iconv_open("UTF-8", nl_langinfo(CODESET
));
207 if (state
== (iconv_t
) -1) return NULL
;
209 /* Get the initial output buffer length */
210 locale_length
= strlen(locale
);
211 buffer_length
= locale_length
+ 1;
213 inbuf
= (char *) locale
;
214 inleft
= locale_length
+ 1;
218 new_buffer
= realloc(buffer
, buffer_length
);
227 outbuf
= buffer
+ buffer_used
;
228 outleft
= buffer_length
- buffer_used
;
230 /* Convert chunk of data */
231 if ((size_t) -1 == iconv(state
, &inbuf
, &inleft
, &outbuf
, &outleft
) &&
237 /* Update positions */
238 buffer_length
+= 1024;
239 buffer_used
= outbuf
- buffer
;
248 /* Determine how many columns occupies given locale encoded string.
249 * Return number of columns or -1 */
250 static int localewidth(const char *string
) {
254 int offset
= 0, columns
= 0;
258 if (!string
|| !*string
) return 0;
260 memset(&mbstate
, 0, sizeof(mbstate
));
261 length
= strlen(string
);
263 /* Step on each multibyte character */
264 for (offset
= 0; offset
< length
;) {
265 wc_size
= mbrtowc(&wide_char
, string
+ offset
, length
, &mbstate
);
267 wchar_width
= wcwidth(wide_char
);
268 if (wchar_width
< 0) break; /* Non-printable character? */
270 columns
+= wchar_width
;
272 } else break; /* L'\0' or incomplete multibyte character */
279 /* Determine how many columns occupies given UTF-8 encoded string.
280 * Return number of columns or -1 */
281 int utf8width(const char *string
) {
285 if (!string
|| !*string
) return 0;
287 locale_string
= utf82locale(string
);
288 if (!locale_string
) return -1;
290 columns
= localewidth(locale_string
);
297 /* Determine how many columns occupies given number.
298 * Return number of columns or -1 */
299 int numberwidth(const size_t number
) {
303 shi_asprintf(&buffer
, "%zu", number
);
304 if (!buffer
) return -1;
306 columns
= localewidth(buffer
);
313 /* Print locale-encoded string occupying exactly given terminal width.
314 * @width is desired column number. If negative, do not limit the width, but
315 * fill to spaces to absolute width if string is shorter */
316 void fnprint(FILE *stream
, const char *locale_string
, int width
) {
317 int abs_width
= abs(width
);
321 int offset
= 0, columns
= 0;
323 size_t locale_length
;
325 if (!locale_string
) {
326 /* FIXME: a space can occupy more than one column */
327 for (columns
= 0; columns
< abs_width
; columns
++) fprintf(stream
, " ");
331 memset(&mbstate
, 0, sizeof(mbstate
));
332 locale_length
= strlen(locale_string
);
334 /* Step on each multibyte character */
335 for (offset
= 0; offset
< locale_length
;) {
336 wc_size
= mbrtowc(&wide_char
, locale_string
+ offset
, locale_length
,
339 wchar_width
= wcwidth(wide_char
);
340 if (wchar_width
< 0) break; /* Non-printable character? */
342 if (columns
+ wchar_width
> abs_width
)
343 /* This character overflows desired display width */
346 columns
+= wchar_width
;
348 } else break; /* L'\0' or incomplete multibyte character */
351 /* Print fitting prefix of locale_string */
353 fprintf(stream
, "%.*s", offset
, locale_string
);
355 fprintf(stream
, "%s", locale_string
);
357 /* And pad to width spaces */
358 /* FIXME: a space can occupy more than one column */
359 while (columns
< abs_width
) {
360 fprintf(stream
, " ");
366 /* Print locale-encoded string occupying at lest given terminal width.
367 * If the string is shorter, it will pad the string with spaces.
368 * If the string is wider, it will move cursor to next line on column with
370 void fhprint(FILE *stream
, const char *locale_string
, size_t width
) {
374 size_t offset
= 0, columns
= 0;
376 size_t locale_length
;
378 if (!locale_string
) return;
380 memset(&mbstate
, 0, sizeof(mbstate
));
381 locale_length
= strlen(locale_string
);
383 /* Step on each multibyte character */
384 for (offset
= 0; offset
< locale_length
;) {
385 wc_size
= mbrtowc(&wide_char
, locale_string
+ offset
, locale_length
,
388 wchar_width
= wcwidth(wide_char
);
389 if (wchar_width
< 0) break; /* Non-printable character? */
391 if (columns
+ wchar_width
> width
)
392 /* This character overflows desired display width */
395 columns
+= wchar_width
;
397 } else break; /* L'\0' or incomplete multibyte character */
400 /* Print fitting prefix of locale_string */
401 fprintf(stream
, "%s", locale_string
);
403 /* FIXME: a space can occupy more than one column */
404 if (columns
< width
) {
405 /* And pad with spaces to width */
406 while (columns
< width
) {
407 fprintf(stream
, " ");
411 /* Shift to next line */
412 fprintf(stream
, "\n");
413 for (columns
= 0; columns
< width
; columns
++) {
414 fprintf(stream
, " ");
420 /* Switches time zone to UTC.
421 * XXX: This is not reentrant and not thread-safe */
422 _hidden
void switch_tz_to_utc(void) {
427 tz_orig
= strdup(tz
);
429 PANIC("Can not back original time zone up");
434 if (setenv("TZ", "", 1))
435 PANIC("Can not change time zone to UTC temporarily");
441 /* Switches time zone to original value.
442 * XXX: This is not reentrant and not thread-safe */
443 _hidden
void switch_tz_to_native(void) {
445 if (setenv("TZ", tz_orig
, 1))
446 PANIC("Can not restore time zone by setting TZ variable");
451 PANIC("Can not restore time zone by unsetting TZ variable");