1 /*
2  * Byte size string functions
3  *
4  * Copyright (C) 2010-2021, Joachim Metz <joachim.metz@gmail.com>
5  *
6  * Refer to AUTHORS for acknowledgements.
7  *
8  * This program is free software: you can redistribute it and/or modify
9  * it under the terms of the GNU Lesser General Public License as published by
10  * the Free Software Foundation, either version 3 of the License, or
11  * (at your option) any later version.
12  *
13  * This program is distributed in the hope that it will be useful,
14  * but WITHOUT ANY WARRANTY; without even the implied warranty of
15  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
16  * GNU General Public License for more details.
17  *
18  * You should have received a copy of the GNU Lesser General Public License
19  * along with this program.  If not, see <https://www.gnu.org/licenses/>.
20  */
21 
22 #include <common.h>
23 #include <narrow_string.h>
24 #include <system_string.h>
25 #include <types.h>
26 #include <wide_string.h>
27 
28 #include "byte_size_string.h"
29 #include "qcowtools_libcerror.h"
30 #include "qcowtools_libclocale.h"
31 #include "qcowtools_libcnotify.h"
32 
33 /* Creates a human readable byte size string
34  * Returns 1 if successful or -1 on error
35  */
byte_size_string_create(system_character_t * byte_size_string,size_t byte_size_string_length,uint64_t size,int units,libcerror_error_t ** error)36 int byte_size_string_create(
37      system_character_t *byte_size_string,
38      size_t byte_size_string_length,
39      uint64_t size,
40      int units,
41      libcerror_error_t **error )
42 {
43 	static char *function = "byte_size_string_create";
44 	int decimal_point     = 0;
45 
46 	if( libclocale_locale_get_decimal_point(
47 	     &decimal_point,
48 	     error ) != 1 )
49 	{
50 		libcerror_error_set(
51 		 error,
52 		 LIBCERROR_ERROR_DOMAIN_RUNTIME,
53 		 LIBCERROR_RUNTIME_ERROR_GET_FAILED,
54 		 "%s: unable to retrieve locale decimal point.",
55 		 function );
56 
57 		return( -1 );
58 	}
59 	if( byte_size_string_create_with_decimal_point(
60 	     byte_size_string,
61 	     byte_size_string_length,
62 	     size,
63 	     units,
64 	     decimal_point,
65 	     error ) != 1 )
66 	{
67 		libcerror_error_set(
68 		 error,
69 		 LIBCERROR_ERROR_DOMAIN_RUNTIME,
70 		 LIBCERROR_RUNTIME_ERROR_SET_FAILED,
71 		 "%s: unable to create byte size string.",
72 		 function );
73 
74 		return( -1 );
75 	}
76 	return( 1 );
77 }
78 
79 /* Creates a human readable byte size string
80  * Returns 1 if successful or -1 on error
81  */
byte_size_string_create_with_decimal_point(system_character_t * byte_size_string,size_t byte_size_string_length,uint64_t size,int units,int decimal_point,libcerror_error_t ** error)82 int byte_size_string_create_with_decimal_point(
83      system_character_t *byte_size_string,
84      size_t byte_size_string_length,
85      uint64_t size,
86      int units,
87      int decimal_point,
88      libcerror_error_t **error )
89 {
90 	const system_character_t *factor_string = NULL;
91 	const system_character_t *units_string  = NULL;
92 	static char *function                   = "byte_size_string_create_with_decimal_point";
93 	ssize_t print_count                     = 0;
94 	uint64_t factored_size                  = 0;
95 	uint64_t last_factored_size             = 0;
96 	int8_t factor                           = 0;
97 	int8_t remainder                        = -1;
98 
99 	if( byte_size_string == NULL )
100 	{
101 		libcerror_error_set(
102 		 error,
103 		 LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
104 		 LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
105 		 "%s: invalid byte size string.",
106 		 function );
107 
108 		return( -1 );
109 	}
110 	/* Minimum of 4 digits and separator, space, 3 letter unit, end of string
111 	 */
112 	if( byte_size_string_length < 9 )
113 	{
114 		libcerror_error_set(
115 		 error,
116 		 LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
117 		 LIBCERROR_ARGUMENT_ERROR_VALUE_TOO_SMALL,
118 		 "%s: byte size string too small.",
119 		 function );
120 
121 		return( -1 );
122 	}
123 	if( ( size < 1024 )
124 	 || ( units == BYTE_SIZE_STRING_UNIT_MEGABYTE ) )
125 	{
126 		units_string = _SYSTEM_STRING( "B" );
127 	}
128 	else if( units == BYTE_SIZE_STRING_UNIT_MEBIBYTE )
129 	{
130 		units_string = _SYSTEM_STRING( "iB" );
131 	}
132 	factored_size = size;
133 
134 	if( factored_size >= (uint64_t) units )
135 	{
136 		while( factored_size >= (uint64_t) units )
137 		{
138 			last_factored_size = factored_size;
139 			factored_size     /= units;
140 
141 			factor++;
142 		}
143 		if( factored_size < 10 )
144 		{
145 			last_factored_size %= units;
146 			remainder           = (int8_t) ( last_factored_size / 100 );
147 		}
148 	}
149 	switch( factor )
150 	{
151 		case 0:
152 			factor_string = _SYSTEM_STRING( "" );
153 			break;
154 
155 		case 1:
156 			factor_string = _SYSTEM_STRING( "K" );
157 			break;
158 
159 		case 2:
160 			factor_string = _SYSTEM_STRING( "M" );
161 			break;
162 
163 		case 3:
164 			factor_string = _SYSTEM_STRING( "G" );
165 			break;
166 
167 		case 4:
168 			factor_string = _SYSTEM_STRING( "T" );
169 			break;
170 
171 		case 5:
172 			factor_string = _SYSTEM_STRING( "P" );
173 			break;
174 
175 		case 6:
176 			factor_string = _SYSTEM_STRING( "E" );
177 			break;
178 
179 		case 7:
180 			factor_string = _SYSTEM_STRING( "Z" );
181 			break;
182 
183 		case 8:
184 			factor_string = _SYSTEM_STRING( "Y" );
185 			break;
186 
187 		default:
188 			libcerror_error_set(
189 			 error,
190 			 LIBCERROR_ERROR_DOMAIN_RUNTIME,
191 			 LIBCERROR_RUNTIME_ERROR_UNSUPPORTED_VALUE,
192 			 "%s: unsupported size factor.",
193 			 function );
194 
195 			return( -1 );
196 	}
197 	if( remainder > 9 )
198 	{
199 		remainder = 9;
200 	}
201 	if( remainder >= 0 )
202 	{
203 		print_count = system_string_sprintf(
204 		               byte_size_string,
205 		               byte_size_string_length,
206 		               _SYSTEM_STRING( "%" ) _SYSTEM_STRING( PRIu64 )
207 		               _SYSTEM_STRING( "%" ) _SYSTEM_STRING( PRIc_SYSTEM )
208 		               _SYSTEM_STRING( "%" ) _SYSTEM_STRING( PRIu8 )
209 		               _SYSTEM_STRING( " %" ) _SYSTEM_STRING( PRIs_SYSTEM )
210 		               _SYSTEM_STRING( "%" ) _SYSTEM_STRING( PRIs_SYSTEM ),
211 		               factored_size,
212 		               (system_character_t) decimal_point,
213 		               remainder,
214 		               factor_string,
215 		               units_string );
216 	}
217 	else
218 	{
219 		print_count = system_string_sprintf(
220 		               byte_size_string,
221 		               byte_size_string_length,
222 		               _SYSTEM_STRING( "%" ) _SYSTEM_STRING( PRIu64 )
223 		               _SYSTEM_STRING( " %" ) _SYSTEM_STRING( PRIs_SYSTEM )
224 		               _SYSTEM_STRING( "%" ) _SYSTEM_STRING( PRIs_SYSTEM ),
225 		               factored_size,
226 		               factor_string,
227 		               units_string );
228 	}
229 	if( ( print_count < 0 )
230 	 || ( (size_t) print_count > byte_size_string_length ) )
231 	{
232 		libcerror_error_set(
233 		 error,
234 		 LIBCERROR_ERROR_DOMAIN_RUNTIME,
235 		 LIBCERROR_RUNTIME_ERROR_SET_FAILED,
236 		 "%s: unable to set byte size string.",
237 		 function );
238 
239 		return( -1 );
240 	}
241 	return( 1 );
242 }
243 
244 /* Converts a human readable byte size string into a value
245  * Returns 1 if successful or -1 on error
246  */
byte_size_string_convert(const system_character_t * byte_size_string,size_t byte_size_string_length,uint64_t * size,libcerror_error_t ** error)247 int byte_size_string_convert(
248      const system_character_t *byte_size_string,
249      size_t byte_size_string_length,
250      uint64_t *size,
251      libcerror_error_t **error )
252 {
253 	static char *function = "byte_size_string_convert";
254 	int decimal_point     = 0;
255 
256 	if( libclocale_locale_get_decimal_point(
257 	     &decimal_point,
258 	     error ) != 1 )
259 	{
260 		libcerror_error_set(
261 		 error,
262 		 LIBCERROR_ERROR_DOMAIN_RUNTIME,
263 		 LIBCERROR_RUNTIME_ERROR_GET_FAILED,
264 		 "%s: unable to retrieve locale decimal point.",
265 		 function );
266 
267 		return( -1 );
268 	}
269 	if( byte_size_string_convert_with_decimal_point(
270 	     byte_size_string,
271 	     byte_size_string_length,
272 	     decimal_point,
273 	     size,
274 	     error ) != 1 )
275 	{
276 		libcerror_error_set(
277 		 error,
278 		 LIBCERROR_ERROR_DOMAIN_RUNTIME,
279 		 LIBCERROR_RUNTIME_ERROR_GET_FAILED,
280 		 "%s: unable to retrieve byte size from string.",
281 		 function );
282 
283 		return( -1 );
284 	}
285 	return( 1 );
286 }
287 
288 /* Converts a human readable byte size string into a value
289  * Returns 1 if successful or -1 on error
290  */
byte_size_string_convert_with_decimal_point(const system_character_t * byte_size_string,size_t byte_size_string_length,int decimal_point,uint64_t * size,libcerror_error_t ** error)291 int byte_size_string_convert_with_decimal_point(
292      const system_character_t *byte_size_string,
293      size_t byte_size_string_length,
294      int decimal_point,
295      uint64_t *size,
296      libcerror_error_t **error )
297 {
298 	static char *function            = "byte_size_string_convert_with_decimal_point";
299 	size_t byte_size_string_iterator = 0;
300 	uint64_t byte_size               = 0;
301 	int8_t factor                    = 0;
302 	int8_t remainder                 = -1;
303 	int units                        = 0;
304 
305 	if( byte_size_string == NULL )
306 	{
307 		libcerror_error_set(
308 		 error,
309 		 LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
310 		 LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
311 		 "%s: invalid byte size string.",
312 		 function );
313 
314 		return( -1 );
315 	}
316 	if( size == NULL )
317 	{
318 		libcerror_error_set(
319 		 error,
320 		 LIBCERROR_ERROR_DOMAIN_ARGUMENTS,
321 		 LIBCERROR_ARGUMENT_ERROR_INVALID_VALUE,
322 		 "%s: invalid size.",
323 		 function );
324 
325 		return( -1 );
326 	}
327 	while( byte_size_string_iterator < byte_size_string_length )
328 	{
329 		if( ( byte_size_string[ byte_size_string_iterator ] < (system_character_t) '0' )
330 		 || ( byte_size_string[ byte_size_string_iterator ] > (system_character_t) '9' ) )
331 		{
332 			break;
333 		}
334 		byte_size *= 10;
335 		byte_size += ( byte_size_string[ byte_size_string_iterator ] - (system_character_t) '0' );
336 
337 		byte_size_string_iterator++;
338 	}
339 	if( byte_size_string[ byte_size_string_iterator ] == (system_character_t) decimal_point )
340 	{
341 		byte_size_string_iterator++;
342 
343 		if( ( byte_size_string[ byte_size_string_iterator ] >= (system_character_t) '0' )
344 		 && ( byte_size_string[ byte_size_string_iterator ] <= (system_character_t) '9' ) )
345 		{
346 			remainder = (int8_t) ( byte_size_string[ byte_size_string_iterator ] - (system_character_t) '0' );
347 
348 			byte_size_string_iterator++;
349 		}
350 		remainder *= 10;
351 
352 		if( ( byte_size_string[ byte_size_string_iterator ] >= (system_character_t) '0' )
353 		 && ( byte_size_string[ byte_size_string_iterator ] <= (system_character_t) '9' ) )
354 		{
355 			remainder += (int8_t) ( byte_size_string[ byte_size_string_iterator ] - (system_character_t) '0' );
356 
357 			byte_size_string_iterator++;
358 		}
359 		/* Ignore more than 2 digits after separator
360 		 */
361 		while( byte_size_string_iterator < byte_size_string_length )
362 		{
363 			if( ( byte_size_string[ byte_size_string_iterator ] < (system_character_t) '0' )
364 			 || ( byte_size_string[ byte_size_string_iterator ] > (system_character_t) '9' ) )
365 			{
366 				break;
367 			}
368 			byte_size_string_iterator++;
369 		}
370 	}
371 	if( byte_size_string[ byte_size_string_iterator ] == (system_character_t) ' ' )
372 	{
373 		byte_size_string_iterator++;
374 	}
375 	switch( byte_size_string[ byte_size_string_iterator ] )
376 	{
377 		case 'k':
378 		case 'K':
379 			factor = 1;
380 			break;
381 
382 		case 'm':
383 		case 'M':
384 			factor = 2;
385 			break;
386 
387 		case 'g':
388 		case 'G':
389 			factor = 3;
390 			break;
391 
392 		case 't':
393 		case 'T':
394 			factor = 4;
395 			break;
396 
397 		case 'p':
398 		case 'P':
399 			factor = 5;
400 			break;
401 
402 		case 'e':
403 		case 'E':
404 			factor = 6;
405 			break;
406 
407 		case 'z':
408 		case 'Z':
409 			factor = 7;
410 			break;
411 
412 		case 'y':
413 		case 'Y':
414 			factor = 8;
415 			break;
416 
417 		default:
418 			break;
419 	}
420 	if( factor >= 1 )
421 	{
422 		byte_size_string_iterator++;
423 	}
424 	if( byte_size_string_iterator >= byte_size_string_length )
425 	{
426 		units = BYTE_SIZE_STRING_UNIT_MEBIBYTE;
427 	}
428 	else if( ( byte_size_string[ byte_size_string_iterator ] == (system_character_t) 'i' )
429 	      && ( byte_size_string[ byte_size_string_iterator + 1 ] == (system_character_t) 'B' ) )
430 	{
431 		units = BYTE_SIZE_STRING_UNIT_MEBIBYTE;
432 
433 		byte_size_string_iterator += 2;
434 	}
435 	else if( byte_size_string[ byte_size_string_iterator ] == (system_character_t) 'B' )
436 	{
437 		units = BYTE_SIZE_STRING_UNIT_MEGABYTE;
438 
439 		byte_size_string_iterator++;
440 	}
441 	else
442 	{
443 		libcerror_error_set(
444 		 error,
445 		 LIBCERROR_ERROR_DOMAIN_RUNTIME,
446 		 LIBCERROR_RUNTIME_ERROR_VALUE_OUT_OF_BOUNDS,
447 		 "%s: invalid units.",
448 		 function );
449 
450 		return( -1 );
451 	}
452 	if( factor > 0 )
453 	{
454 		if( remainder > 0 )
455 		{
456 			byte_size *= units;
457 
458 			factor--;
459 
460 			byte_size += ( remainder * 10 );
461 		}
462 		for( ; factor > 0; factor-- )
463 		{
464 			byte_size *= units;
465 		}
466 	}
467 #if defined( HAVE_VERBOSE_OUTPUT )
468 	else if( remainder >= 0 )
469 	{
470 		libcnotify_printf(
471 		 "%s: ignoring byte value remainder.\n",
472 		 function );
473 	}
474 #endif
475 #if defined( HAVE_VERBOSE_OUTPUT )
476 	if( ( byte_size_string[ byte_size_string_iterator ] != 0 )
477 	 && ( byte_size_string[ byte_size_string_iterator ] != (system_character_t) ' ' )
478 	 && ( byte_size_string[ byte_size_string_iterator ] != (system_character_t) '\n' )
479 	 && ( byte_size_string[ byte_size_string_iterator ] != (system_character_t) '\r' ) )
480 	{
481 		libcnotify_printf(
482 		 "%s: trailing data in byte size string.\n",
483 		 function );
484 	}
485 #endif
486 	*size = byte_size;
487 
488 	return( 1 );
489 }
490 
491