Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion configure.ac
Original file line number Diff line number Diff line change
Expand Up @@ -46,7 +46,7 @@ LIBBYTESIZE_PKG_CHECK_MODULES([PCRE2], [libpcre2-8])

AC_CHECK_LIB(gmp, __gmpz_init)

AC_CHECK_HEADERS([langinfo.h gmp.h mpfr.h stdint.h stdbool.h stdarg.h string.h stdio.h ctype.h],
AC_CHECK_HEADERS([langinfo.h gmp.h stdint.h stdbool.h stdarg.h string.h stdio.h ctype.h],
[],
[LIBBYTESIZE_SOFT_FAILURE([Header file $ac_header not found.])],
[])
Expand Down
2 changes: 1 addition & 1 deletion po/libbytesize.pot
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ msgid ""
msgstr ""
"Project-Id-Version: libbytesize 2.12\n"
"Report-Msgid-Bugs-To: [email protected]\n"
"POT-Creation-Date: 2025-12-04 12:49+0100\n"
"POT-Creation-Date: 2026-01-08 06:02-0800\n"
"PO-Revision-Date: YEAR-MO-DA HO:MI+ZONE\n"
"Last-Translator: FULL NAME <EMAIL@ADDRESS>\n"
"Language-Team: LANGUAGE <[email protected]>\n"
Expand Down
2 changes: 1 addition & 1 deletion src/Makefile.am
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@ LDADD = $(LIBINTL)

lib_LTLIBRARIES = libbytesize.la
libbytesize_la_CFLAGS = -Wall -Wextra -Werror -Wno-overflow -D_GNU_SOURCE
libbytesize_la_LIBADD = -lgmp -lmpfr $(PCRE2_LIBS)
libbytesize_la_LIBADD = -lgmp $(PCRE2_LIBS)
libbytesize_la_LDFLAGS = -version-info 1:0:0
libbytesize_la_SOURCES = bs_size.c bs_size.h gettext.h

Expand Down
181 changes: 136 additions & 45 deletions src/bs_size.c
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
#include <gmp.h>
#include <mpfr.h>
#include <langinfo.h>
#include <stdarg.h>
#include <stdio.h>
#include <stdlib.h>
#include <inttypes.h>
#include <string.h>
#include <ctype.h>
Expand Down Expand Up @@ -224,53 +224,58 @@ static void strstrip(char *str) {
str[i-begin] = '\0';
}

static bool multiply_size_by_unit (mpfr_t size, char *unit_str) {
static bool multiply_size_by_unit (mpq_t size, char *unit_str) {
BSBunit bunit = BS_BUNIT_UNDEF;
BSDunit dunit = BS_DUNIT_UNDEF;
uint64_t pwr = 0;
mpfr_t dec_mul;
mpq_t dec_mul;
mpz_t pow_1000;
size_t unit_str_len = 0;

unit_str_len = strlen (unit_str);

for (bunit=BS_BUNIT_B; bunit < BS_BUNIT_UNDEF; bunit++)
if (strncasecmp (unit_str, b_units[bunit-BS_BUNIT_B], unit_str_len) == 0) {
pwr = (uint64_t) bunit - BS_BUNIT_B;
mpfr_mul_2exp (size, size, 10 * pwr, MPFR_RNDN);
mpz_mul_2exp (mpq_numref (size), mpq_numref (size), 10 * pwr);
return true;
}

mpfr_init2 (dec_mul, BS_FLOAT_PREC_BITS);
mpfr_set_ui (dec_mul, 1000, MPFR_RNDN);
mpq_init (dec_mul);
mpz_init (pow_1000);
for (dunit=BS_DUNIT_B; dunit < BS_DUNIT_UNDEF; dunit++)
if (strncasecmp (unit_str, d_units[dunit-BS_DUNIT_B], unit_str_len) == 0) {
pwr = (uint64_t) (dunit - BS_DUNIT_B);
mpfr_pow_ui (dec_mul, dec_mul, pwr, MPFR_RNDN);
mpfr_mul (size, size, dec_mul, MPFR_RNDN);
mpfr_clear (dec_mul);
mpz_ui_pow_ui (pow_1000, 1000, pwr);
mpq_set_z (dec_mul, pow_1000);
mpq_mul (size, size, dec_mul);
mpz_clear (pow_1000);
mpq_clear (dec_mul);
return true;
}

/* not found among the binary and decimal units, let's try their translated
versions */
for (bunit=BS_BUNIT_B; bunit < BS_BUNIT_UNDEF; bunit++)
if (strncasecmp (unit_str, _(b_units[bunit-BS_BUNIT_B]), unit_str_len) == 0) {
pwr = (uint64_t) bunit - BS_BUNIT_B;
mpfr_mul_2exp (size, size, 10 * pwr, MPFR_RNDN);
mpz_mul_2exp (mpq_numref (size), mpq_numref (size), 10 * pwr);
mpz_clear (pow_1000);
mpq_clear (dec_mul);
return true;
}

mpfr_init2 (dec_mul, BS_FLOAT_PREC_BITS);
mpfr_set_ui (dec_mul, 1000, MPFR_RNDN);
for (dunit=BS_DUNIT_B; dunit < BS_DUNIT_UNDEF; dunit++)
if (strncasecmp (unit_str, _(d_units[dunit-BS_DUNIT_B]), unit_str_len) == 0) {
pwr = (uint64_t) (dunit - BS_DUNIT_B);
mpfr_pow_ui (dec_mul, dec_mul, pwr, MPFR_RNDN);
mpfr_mul (size, size, dec_mul, MPFR_RNDN);
mpfr_clear (dec_mul);
mpz_ui_pow_ui (pow_1000, 1000, pwr);
mpq_set_z (dec_mul, pow_1000);
mpq_mul (size, size, dec_mul);
mpz_clear (pow_1000);
mpq_clear (dec_mul);
return true;
}

mpz_clear (pow_1000);
mpq_clear (dec_mul);
return false;
}

Expand Down Expand Up @@ -436,10 +441,10 @@ BSSize bs_size_new_from_bytes (uint64_t bytes, int sgn) {
*/
BSSize bs_size_new_from_str (const char *size_str, BSError **error) {
char const * const pattern = "^\\s* # white space \n" \
"(?P<numeric> # the numeric part consists of three parts, below \n" \
" (-|\\+)? # optional sign character \n" \
" (?P<base>([0-9\\.%s]+)) # base \n" \
" (?P<exp>(e|E)(-|\\+)?[0-9]+)?) # exponent \n" \
"(?P<sign>(-|\\+)?) # optional sign character \n" \
"(?P<int_part>[0-9]*) # integer part \n" \
"(?:%s(?P<frac_part>[0-9]*))? # optional fractional part \n" \
"(?:(?P<exp_sep>[eE])(?P<exp_sign>(-|\\+)?)(?P<exp_val>[0-9]+))? # optional exponent \n" \
"\\s* # white space \n" \
"(?P<rest>[^\\s]*)\\s*$ # unit specification";
char *real_pattern = NULL;
Expand All @@ -451,19 +456,25 @@ BSSize bs_size_new_from_str (const char *size_str, BSError **error) {
int str_count = 0;
const char *radix_char = NULL;
char *loc_size_str = NULL;
mpf_t parsed_size;
mpfr_t size;
mpq_t size;
int status = 0;
BSSize ret = NULL;
PCRE2_UCHAR *substring = NULL;
PCRE2_SIZE substring_len = 0;
PCRE2_UCHAR error_buffer[ERROR_BUFFER_LEN];
int sign = 1;
long exp_val = 0;
int exp_sign = 1;
mpz_t numerator, denominator, int_part, frac_part, pow_10;
size_t frac_digits = 0;
bool has_int_digits = false;
bool has_frac_digits = false;

radix_char = nl_langinfo (RADIXCHAR);
if (strncmp (radix_char, ".", 1) != 0)
real_pattern = strdup_printf (pattern, radix_char);
else
real_pattern = strdup_printf (pattern, "");
real_pattern = strdup_printf (pattern, "\\.");

regex = pcre2_compile ((PCRE2_SPTR) real_pattern, PCRE2_ZERO_TERMINATED, PCRE2_EXTENDED, &errorcode, &erroffset, NULL);
free (real_pattern);
Expand Down Expand Up @@ -509,32 +520,111 @@ BSSize bs_size_new_from_str (const char *size_str, BSError **error) {
return NULL;
}

status = pcre2_substring_get_byname (match_data, (PCRE2_SPTR) "numeric", &substring, &substring_len);
if (status < 0 || substring_len < 1) {
set_error (error, BS_ERROR_INVALID_SPEC, strdup_printf ("Failed to parse size spec: %s", size_str));
pcre2_match_data_free (match_data);
pcre2_code_free (regex);
free (loc_size_str);
return NULL;
mpq_init (size);
mpz_init (numerator);
mpz_init (denominator);
mpz_init (int_part);
mpz_init (frac_part);
mpz_init (pow_10);

status = pcre2_substring_get_byname (match_data, (PCRE2_SPTR) "sign", &substring, &substring_len);
if (status >= 0 && substring_len > 0 && substring[0] == '-')
sign = -1;
if (status >= 0)
pcre2_substring_free (substring);

status = pcre2_substring_get_byname (match_data, (PCRE2_SPTR) "int_part", &substring, &substring_len);
if (status >= 0 && substring_len > 0) {
has_int_digits = true;
status = mpz_set_str (int_part, (const char *) substring, 10);
pcre2_substring_free (substring);
if (status != 0) {
set_error (error, BS_ERROR_INVALID_SPEC, strdup_printf ("Failed to parse size spec: %s", size_str));
mpz_clears (numerator, denominator, int_part, frac_part, pow_10, NULL);
mpq_clear (size);
pcre2_match_data_free (match_data);
pcre2_code_free (regex);
free (loc_size_str);
return NULL;
}
} else {
mpz_set_ui (int_part, 0);
if (status >= 0)
pcre2_substring_free (substring);
}

/* parse the number using GMP because it knows how to handle localization
much better than MPFR */
mpf_init2 (parsed_size, BS_FLOAT_PREC_BITS);
status = mpf_set_str (parsed_size, *substring == '+' ? (const char *) substring+1 : (const char *) substring, 10);
pcre2_substring_free (substring);
if (status != 0) {
status = pcre2_substring_get_byname (match_data, (PCRE2_SPTR) "frac_part", &substring, &substring_len);
if (status >= 0 && substring_len > 0) {
has_frac_digits = true;
frac_digits = substring_len;
status = mpz_set_str (frac_part, (const char *) substring, 10);
pcre2_substring_free (substring);
if (status != 0) {
set_error (error, BS_ERROR_INVALID_SPEC, strdup_printf ("Failed to parse size spec: %s", size_str));
mpz_clears (numerator, denominator, int_part, frac_part, pow_10, NULL);
mpq_clear (size);
pcre2_match_data_free (match_data);
pcre2_code_free (regex);
free (loc_size_str);
return NULL;
}
} else {
mpz_set_ui (frac_part, 0);
frac_digits = 0;
if (status >= 0)
pcre2_substring_free (substring);
}

/* Validate: we must have at least one digit in int_part or frac_part */
if (!has_int_digits && !has_frac_digits) {
set_error (error, BS_ERROR_INVALID_SPEC, strdup_printf ("Failed to parse size spec: %s", size_str));
mpz_clears (numerator, denominator, int_part, frac_part, pow_10, NULL);
mpq_clear (size);
pcre2_match_data_free (match_data);
pcre2_code_free (regex);
free (loc_size_str);
mpf_clear (parsed_size);
return NULL;
}
/* but use MPFR from now on because GMP thinks 0.1*1000 = 99 */
mpfr_init2 (size, BS_FLOAT_PREC_BITS);
mpfr_set_f (size, parsed_size, MPFR_RNDN);
mpf_clear (parsed_size);

status = pcre2_substring_get_byname (match_data, (PCRE2_SPTR) "exp_val", &substring, &substring_len);
if (status >= 0 && substring_len > 0) {
exp_val = strtol ((const char *) substring, NULL, 10);
pcre2_substring_free (substring);

status = pcre2_substring_get_byname (match_data, (PCRE2_SPTR) "exp_sign", &substring, &substring_len);
if (status >= 0 && substring_len > 0 && substring[0] == '-')
exp_sign = -1;
if (status >= 0)
pcre2_substring_free (substring);
}

mpz_ui_pow_ui (pow_10, 10, frac_digits);
mpz_set (denominator, pow_10);
mpz_mul (numerator, int_part, pow_10);
mpz_add (numerator, numerator, frac_part);

if (exp_val != 0) {
long adjusted_exp = exp_val;
if (exp_sign == -1)
adjusted_exp = -adjusted_exp;

if (adjusted_exp > 0) {
mpz_ui_pow_ui (pow_10, 10, adjusted_exp);
mpz_mul (numerator, numerator, pow_10);
} else if (adjusted_exp < 0) {
mpz_ui_pow_ui (pow_10, 10, -adjusted_exp);
mpz_mul (denominator, denominator, pow_10);
}
}

if (sign == -1)
mpz_neg (numerator, numerator);

mpq_set_num (size, numerator);
mpq_set_den (size, denominator);
mpq_canonicalize (size);

mpz_clears (numerator, denominator, int_part, frac_part, pow_10, NULL);

status = pcre2_substring_get_byname (match_data, (PCRE2_SPTR) "rest", &substring, &substring_len);
if ((status >= 0) && strncmp ((const char *) substring, "", 1) != 0) {
Expand All @@ -545,7 +635,7 @@ BSSize bs_size_new_from_str (const char *size_str, BSError **error) {
pcre2_match_data_free (match_data);
pcre2_code_free (regex);
free (loc_size_str);
mpfr_clear (size);
mpq_clear (size);
return NULL;
}
}
Expand All @@ -554,10 +644,11 @@ BSSize bs_size_new_from_str (const char *size_str, BSError **error) {
pcre2_code_free (regex);

ret = bs_size_new ();
mpfr_get_z (ret->bytes, size, MPFR_RNDZ);
/* Rational to int, round towards zero for preserving previous behaviour */
mpz_tdiv_q (ret->bytes, mpq_numref (size), mpq_denref (size));

free (loc_size_str);
mpfr_clear (size);
mpq_clear (size);

return ret;
}
Expand Down
2 changes: 1 addition & 1 deletion src/bytesize.pc.in
Original file line number Diff line number Diff line change
Expand Up @@ -9,4 +9,4 @@ URL: https://github.com/storaged-project/libbytesize
Version: @VERSION@
Cflags: -I${includedir}/bytesize
Libs: -lbytesize
Libs.private: -lgmp -lmpfr
Libs.private: -lgmp
Loading