]> icculus.org git repositories - icculus/xz.git/blob - src/xz/util.c
Some xz man changes.
[icculus/xz.git] / src / xz / util.c
1 ///////////////////////////////////////////////////////////////////////////////
2 //
3 /// \file       util.c
4 /// \brief      Miscellaneous utility functions
5 //
6 //  Author:     Lasse Collin
7 //
8 //  This file has been put into the public domain.
9 //  You can do whatever you want with this file.
10 //
11 ///////////////////////////////////////////////////////////////////////////////
12
13 #include "private.h"
14
15
16 // Thousand separator for format strings is not supported outside POSIX.
17 // This is used in uint64_to_str() and double_to_str().
18 #ifdef DOSLIKE
19 #       define THOUSAND ""
20 #else
21 #       define THOUSAND "'"
22 #endif
23
24
25 extern void *
26 xrealloc(void *ptr, size_t size)
27 {
28         assert(size > 0);
29
30         ptr = realloc(ptr, size);
31         if (ptr == NULL)
32                 message_fatal("%s", strerror(errno));
33
34         return ptr;
35 }
36
37
38 extern char *
39 xstrdup(const char *src)
40 {
41         assert(src != NULL);
42         const size_t size = strlen(src) + 1;
43         char *dest = xmalloc(size);
44         return memcpy(dest, src, size);
45 }
46
47
48 extern uint64_t
49 str_to_uint64(const char *name, const char *value, uint64_t min, uint64_t max)
50 {
51         uint64_t result = 0;
52
53         // Skip blanks.
54         while (*value == ' ' || *value == '\t')
55                 ++value;
56
57         // Accept special value "max". Supporting "min" doesn't seem useful.
58         if (strcmp(value, "max") == 0)
59                 return max;
60
61         if (*value < '0' || *value > '9')
62                 message_fatal(_("%s: Value is not a non-negative "
63                                 "decimal integer"), value);
64
65         do {
66                 // Don't overflow.
67                 if (result > (UINT64_MAX - 9) / 10)
68                         goto error;
69
70                 result *= 10;
71                 result += *value - '0';
72                 ++value;
73         } while (*value >= '0' && *value <= '9');
74
75         if (*value != '\0') {
76                 // Look for suffix.
77                 static const struct {
78                         const char name[4];
79                         uint64_t multiplier;
80                 } suffixes[] = {
81                         { "k",   UINT64_C(1000) },
82                         { "kB",  UINT64_C(1000) },
83                         { "M",   UINT64_C(1000000) },
84                         { "MB",  UINT64_C(1000000) },
85                         { "G",   UINT64_C(1000000000) },
86                         { "GB",  UINT64_C(1000000000) },
87                         { "Ki",  UINT64_C(1024) },
88                         { "KiB", UINT64_C(1024) },
89                         { "Mi",  UINT64_C(1048576) },
90                         { "MiB", UINT64_C(1048576) },
91                         { "Gi",  UINT64_C(1073741824) },
92                         { "GiB", UINT64_C(1073741824) }
93                 };
94
95                 uint64_t multiplier = 0;
96                 for (size_t i = 0; i < ARRAY_SIZE(suffixes); ++i) {
97                         if (strcmp(value, suffixes[i].name) == 0) {
98                                 multiplier = suffixes[i].multiplier;
99                                 break;
100                         }
101                 }
102
103                 if (multiplier == 0) {
104                         message(V_ERROR, _("%s: Invalid multiplier suffix. "
105                                         "Valid suffixes:"), value);
106                         message_fatal("`k' (10^3), `M' (10^6), `G' (10^9) "
107                                         "`Ki' (2^10), `Mi' (2^20), "
108                                         "`Gi' (2^30)");
109                 }
110
111                 // Don't overflow here either.
112                 if (result > UINT64_MAX / multiplier)
113                         goto error;
114
115                 result *= multiplier;
116         }
117
118         if (result < min || result > max)
119                 goto error;
120
121         return result;
122
123 error:
124         message_fatal(_("Value of the option `%s' must be in the range "
125                                 "[%" PRIu64 ", %" PRIu64 "]"),
126                                 name, min, max);
127 }
128
129
130 extern const char *
131 uint64_to_str(uint64_t value, uint32_t slot)
132 {
133         // 2^64 with thousand separators is 26 bytes plus trailing '\0'.
134         static char bufs[4][32];
135
136         assert(slot < ARRAY_SIZE(bufs));
137
138         snprintf(bufs[slot], sizeof(bufs[slot]), "%" THOUSAND PRIu64, value);
139         return bufs[slot];
140 }
141
142
143 extern const char *
144 double_to_str(double value)
145 {
146         // 64 bytes is surely enough, since it won't fit in some other
147         // fields anyway.
148         static char buf[64];
149
150         snprintf(buf, sizeof(buf), "%" THOUSAND ".1f", value);
151         return buf;
152 }
153
154
155 /*
156 /// \brief      Simple quoting to get rid of ASCII control characters
157 ///
158 /// This is not so cool and locale-dependent, but should be good enough
159 /// At least we don't print any control characters on the terminal.
160 ///
161 extern char *
162 str_quote(const char *str)
163 {
164         size_t dest_len = 0;
165         bool has_ctrl = false;
166
167         while (str[dest_len] != '\0')
168                 if (*(unsigned char *)(str + dest_len++) < 0x20)
169                         has_ctrl = true;
170
171         char *dest = malloc(dest_len + 1);
172         if (dest != NULL) {
173                 if (has_ctrl) {
174                         for (size_t i = 0; i < dest_len; ++i)
175                                 if (*(unsigned char *)(str + i) < 0x20)
176                                         dest[i] = '?';
177                                 else
178                                         dest[i] = str[i];
179
180                         dest[dest_len] = '\0';
181
182                 } else {
183                         // Usually there are no control characters,
184                         // so we can optimize.
185                         memcpy(dest, str, dest_len + 1);
186                 }
187         }
188
189         return dest;
190 }
191 */
192
193
194 extern bool
195 is_empty_filename(const char *filename)
196 {
197         if (filename[0] == '\0') {
198                 message_error(_("Empty filename, skipping"));
199                 return true;
200         }
201
202         return false;
203 }
204
205
206 extern bool
207 is_tty_stdin(void)
208 {
209         const bool ret = isatty(STDIN_FILENO);
210
211         if (ret)
212                 message_error(_("Compressed data not read from a terminal "
213                                 "unless `--force' is used."));
214
215         return ret;
216 }
217
218
219 extern bool
220 is_tty_stdout(void)
221 {
222         const bool ret = isatty(STDOUT_FILENO);
223
224         if (ret)
225                 message_error(_("Compressed data not written to a terminal "
226                                 "unless `--force' is used."));
227
228         return ret;
229 }