xref: /third_party/alsa-utils/alsamixer/utils.c (revision c72fcc34)
1/*
2 * utils.c - multibyte-string helpers
3 * Copyright (c) Clemens Ladisch <clemens@ladisch.de>
4 *
5 * This program is free software: you can redistribute it and/or modify
6 * it under the terms of the GNU General Public License as published by
7 * the Free Software Foundation, either version 2 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
17 */
18
19#ifndef _XOPEN_SOURCE
20#define _XOPEN_SOURCE
21#endif
22#include "aconfig.h"
23#include <limits.h>
24#include <stdlib.h>
25#include <string.h>
26#include <wchar.h>
27#include <errno.h>
28#include <stdio.h>
29#include "utils.h"
30#include "mem.h"
31
32/*
33 * mbs_at_width - compute screen position in a string
34 *
35 * For displaying strings on the screen, we have to know how many character
36 * cells are occupied.  This function calculates the position in a multibyte
37 * string that is at a desired position.
38 *
39 * Parameters:
40 * s:     the string
41 * width: on input, the desired number of character cells; on output, the actual
42 *        position, in character cells, of the return value
43 * dir:   -1 or 1; in which direction to round if a multi-column character goes
44 *        over the desired width
45 *
46 * Return value:
47 * Pointer to the place in the string that is as near the desired width as
48 * possible.  If the string is too short, the return value points to the
49 * terminating zero.  If the last character is a multi-column character that
50 * goes over the desired width, the return value may be one character cell
51 * earlier or later than desired, depending on the dir parameter.
52 * In any case, the return value points after any zero-width characters that
53 * follow the last character.
54 */
55const char *mbs_at_width(const char *s, int *width, int dir)
56{
57	size_t len;
58	wchar_t wc;
59	int bytes;
60	int width_so_far, w;
61
62	if (*width <= 0)
63		return s;
64	mbtowc(NULL, NULL, 0); /* reset shift state */
65	len = strlen(s);
66	width_so_far = 0;
67	while (len && (bytes = mbtowc(&wc, s, len)) > 0) {
68		w = wcwidth(wc);
69		if (width_so_far + w > *width && dir < 0)
70			break;
71		if (w >= 0)
72			width_so_far += w;
73		s += bytes;
74		len -= bytes;
75		if (width_so_far >= *width) {
76			while (len && (bytes = mbtowc(&wc, s, len)) > 0) {
77				w = wcwidth(wc);
78				if (w != 0)
79					break;
80				s += bytes;
81				len -= bytes;
82			}
83			break;
84		}
85	}
86	*width = width_so_far;
87	return s;
88}
89
90/*
91 * get_mbs_width - compute screen width of a string
92 */
93unsigned int get_mbs_width(const char *s)
94{
95	int width;
96
97	width = INT_MAX;
98	mbs_at_width(s, &width, 1);
99	return width;
100}
101
102/*
103 * get_max_mbs_width - get width of longest string in an array
104 */
105unsigned int get_max_mbs_width(const char *const *s, unsigned int count)
106{
107	unsigned int max_width, i, len;
108
109	max_width = 0;
110	for (i = 0; i < count; ++i) {
111		len = get_mbs_width(s[i]);
112		if (len > max_width)
113			max_width = len;
114	}
115	return max_width;
116}
117
118#define MAX_FILE_SIZE 1048576
119char *read_file(const char *file_name, unsigned int *file_size)
120{
121	FILE *f;
122	int err;
123	char *buf;
124	unsigned int allocated = 2048;
125	unsigned int bytes_read;
126
127	f = fopen(file_name, "r");
128	if (!f) {
129		err = errno;
130		errno = err;
131		return NULL;
132	}
133	*file_size = 0;
134	buf = NULL;
135	do {
136		allocated *= 2;
137		buf = crealloc(buf, allocated);
138		bytes_read = fread(buf + *file_size, 1, allocated - *file_size, f);
139		*file_size += bytes_read;
140	} while (*file_size == allocated && allocated < MAX_FILE_SIZE);
141	fclose(f);
142	if (*file_size > 0 && buf[*file_size - 1] != '\n' && *file_size < allocated) {
143		buf[*file_size] = '\n';
144		++*file_size;
145	}
146	return buf;
147}
148
149