54#include <glib/gstdio.h>
63#if defined __APPLE__ || defined _POSIX_C_SOURCE >= 1 || defined _XOPEN_SOURCE || defined _BSD_SOURCE \
64 || defined _SVID_SOURCE || defined _POSIX_SOURCE || defined __DragonFly__ || defined __FreeBSD__ \
65 || defined __NetBSD__ || defined __OpenBSD__
79#include <glib/gi18n.h>
88#include <librsvg/rsvg.h>
91#include <librsvg/rsvg-cairo.h>
96 const size_t chunk_size = 64 * 1024;
97 unsigned char *buffers = malloc(2 * chunk_size);
104 first_read = fread(buffers, 1, chunk_size, first);
105 const size_t second_read = fread(buffers + chunk_size, 1, chunk_size, second);
106 if(ferror(first) || ferror(second))
111 result = first_read == second_read && memcmp(buffers, buffers + chunk_size, first_read) == 0;
112 }
while(result == 1 && first_read == chunk_size);
119 return str ? strlen(str) : 0;
126 va_start(args, format);
127 const size_t clen = str ? strlen(str) : 0;
128 const int alen = g_vsnprintf(NULL, 0, format, args);
129 const int nsize = alen + clen + 1;
132 ns = g_realloc(str, nsize);
137 va_start(args, format);
138 g_vsnprintf(ns + clen, alen + 1, format, args);
141 ns[nsize - 1] =
'\0';
149 if(haystack && needle)
151 const gchar *
p = haystack;
152 if((
p = g_strstr_len(
p, strlen(
p), needle)) != NULL)
157 }
while((
p = g_strstr_len((
p + 1), strlen(
p + 1), needle)) != NULL);
166 gchar *nstring = NULL;
170 nstring = g_malloc_n(strlen(
string) + (occurrences * strlen(substitute)) + 1,
sizeof(gchar));
171 const gchar *pend =
string + strlen(
string);
172 const gchar *s = string, *
p = string;
174 if((s = g_strstr_len(s, strlen(s), pattern)) != NULL)
178 memcpy(np,
p, s -
p);
180 memcpy(np, substitute, strlen(substitute));
181 np += strlen(substitute);
182 p = s + strlen(pattern);
183 }
while((s = g_strstr_len((s + 1), strlen(s + 1), pattern)) != NULL);
185 memcpy(np,
p, pend -
p);
189 nstring = g_strdup(
string);
197 const unsigned int count = g_list_length(
items);
198 gchar *result = NULL;
201 gchar **strings = g_malloc0_n(count + 1,
sizeof(gchar *));
207 strings[
i++] =
items->data;
212 result = g_strjoinv(separator, strings);
225 GList *last_item = NULL;
227 items = g_list_sort(
items, (GCompareFunc)g_strcmp0);
231 gchar *
value = (gchar *)iter->data;
232 if(!g_strcmp0(last,
value))
243 iter = g_list_next(iter);
261 const size_t len = strlen(path);
272 if(len > 1 && path[1] !=
'/')
274 while(path[off] !=
'\0' && path[off] !=
'/')
279 user = g_strndup(path + 1, off - 1);
287 return g_strdup(path);
290 rpath = g_build_filename(home_path, path + off, NULL);
295 rpath = g_strdup(path);
318 register const gchar *s = src;
319 while(s - src <
n && *s)
321 s = g_utf8_next_char(s);
327 s = g_utf8_prev_char(s);
328 strncpy(dest, src, s - src);
329 dest[s - src] =
'\0';
333 s = g_utf8_next_char(s);
339 strncpy(dest, src, s - src);
340 dest[s - src] =
'\0';
347 if(g_access(filename, R_OK))
return FALSE;
349 struct _stati64 stats;
356 wchar_t *wfilename = g_utf8_to_utf16(filename, -1, NULL, NULL, NULL);
357 const int result = _wstati64(wfilename, &stats);
359 if(result)
return FALSE;
362 if(stat(filename, &stats))
return FALSE;
365 const gboolean regular = (S_ISREG(stats.st_mode)) != 0;
366 const gboolean size_ok = stats.st_size > 0;
368 return regular && size_ok;
375 struct _stati64 stats;
377 wchar_t *wpath = g_utf8_to_utf16(path, -1, NULL, NULL, NULL);
378 const int result = _wstati64(wpath, &stats);
387 if(stat(path, &stats))
return FALSE;
389 if(S_ISDIR(stats.st_mode) == 0)
return FALSE;
390 if(g_access(path, W_OK | X_OK) != 0)
return FALSE;
399 return g_file_test(dir, G_FILE_TEST_IS_DIR);
405 GDir *dir = g_dir_open(dirname, 0, NULL);
408 while(g_dir_read_name(dir) != NULL)
423 if(g_utf8_validate(
string, -1, NULL))
424 tag = g_strdup(
string);
426 tag = g_convert(
string, -1,
"UTF-8",
"LATIN1", NULL, NULL, NULL);
430 tag = g_strdup(
string);
434 if((*c < 0x20) || (*c >= 0x7f)) *c =
'?';
444 const int a = Y % 19;
445 const int b = Y / 100;
446 const int c = Y % 100;
449 const int f = (b + 8) / 25;
450 const int g = (b -
f + 1) / 3;
451 const int h = (19*a + b -
d -
g + 15) % 30;
454 const int L = (32 + 2*e + 2*
i - h -
k) % 7;
455 const int m = (a + 11*h + 22*
L) / 451;
456 *month = (h +
L - 7*
m + 114) / 31;
457 *
day = ((h +
L - 7*
m + 114) % 31) + 1;
466 localtime_r(&now, <);
469 if((lt.tm_mon == 9 && lt.tm_mday == 31) || (lt.tm_mon == 10 && lt.tm_mday == 1))
477 struct tm easter_sunday = lt;
478 easter(lt.tm_year+1900, &easter_sunday.tm_mon, &easter_sunday.tm_mday);
479 easter_sunday.tm_mon--;
480 easter_sunday.tm_hour = easter_sunday.tm_min = easter_sunday.tm_sec = 0;
481 easter_sunday.tm_isdst = -1;
482 time_t easter_sunday_sec = mktime(&easter_sunday);
492 GError *
error = NULL;
493 cairo_surface_t *surface = NULL;
497 char *dtlogo = g_build_filename(datadir,
"pixmaps", logo, NULL);
498 RsvgHandle *svg = rsvg_handle_new_from_file(dtlogo, &
error);
510 const int stride = cairo_format_stride_for_width(CAIRO_FORMAT_ARGB32, final_width);
512 guint8 *image_buffer = (guint8 *)calloc(stride * final_height,
sizeof(guint8));
517 final_height, stride);
518 if(cairo_surface_status(surface) != CAIRO_STATUS_SUCCESS)
520 fprintf(stderr,
"warning: can't load darktable logo from SVG file `%s'\n", dtlogo);
521 cairo_surface_destroy(surface);
527 cairo_t *cr = cairo_create(surface);
531 cairo_surface_flush(surface);
537 fprintf(stderr,
"warning: can't load darktable logo from SVG file `%s'\n%s\n", dtlogo,
error->message);
552 logo = g_strdup_printf(
"idbutton-%d.svg", (
int)season);
554 logo = g_strdup(
"idbutton.svg");
568#define OSD_COORDINATES_CHR_N "N"
569#define OSD_COORDINATES_CHR_S "S"
570#define OSD_COORDINATES_CHR_E "E"
571#define OSD_COORDINATES_CHR_W "W"
580 float integral, fractional;
582 if(isnan(latitude))
return NULL;
586 latitude = fabsf(latitude);
590 fractional = modff(latitude, &integral);
592 return g_strdup_printf(
"%s %02d\302\260 %06.3f'", c, (
int)integral, fractional*60.0);
598 float integral, fractional;
600 if(isnan(longitude))
return NULL;
604 longitude = fabsf(longitude);
608 fractional = modff(longitude, &integral);
610 return g_strdup_printf(
"%s %03d\302\260 %06.3f'", c, (
int)integral, fractional*60.0);
617 if(isnan(elevation))
return NULL;
621 elevation = fabsf(elevation);
625 return g_strdup_printf(
"%.2f %s %s", elevation, _(
"m"), _(c));
635 gchar dir = toupper(input[strlen(input) - 1]);
636 gchar **list = g_strsplit(input,
",", 0);
640 res = g_ascii_strtoll(list[0], NULL, 10) + (g_ascii_strtod(list[1], NULL) / 60.0);
641 else if(list[3] == NULL)
642 res = g_ascii_strtoll(list[0], NULL, 10) + (g_ascii_strtoll(list[1], NULL, 10) / 60.0)
643 + (g_ascii_strtoll(list[2], NULL, 10) / 3600.0);
644 if(dir ==
'S' || dir ==
'W')
res *= -1.0;
651 const double r1_2,
const double r2_1,
const double r2_2,
char sign,
657 double num, den,
min, sec;
660 if(den == 0)
return FALSE;
665 if(den == 0)
return FALSE;
680 if(sec != -1.0)
res += sec / 3600.0;
693 const double num = r_1;
694 const double den = r_2;
695 if(den == 0)
return FALSE;
710 if(g_utf8_validate(_input, -1, NULL))
711 input = g_strdup(_input);
714 input = g_locale_to_utf8(_input, -1, NULL, NULL, NULL);
718 const gchar *input = _input;
721 gchar *filename = g_filename_from_uri(input, NULL, NULL);
725 if(g_str_has_prefix(input,
"file://"))
728 input += strlen(
"file://");
729 filename = g_uri_unescape_string(input, NULL);
732 filename = g_strdup(input);
739 if(g_path_is_absolute(filename) ==
FALSE)
741 char *current_dir = g_get_current_dir();
742 char *tmp_filename = g_build_filename(current_dir, filename, NULL);
764 GFile *gfile = g_file_new_for_path(filename);
768 filename = g_file_get_path(gfile);
769 g_object_unref(gfile);
773 const char first = g_ascii_toupper(filename[0]);
774 if(first >=
'A' && first <=
'Z' && filename[1] ==
':')
779 else if(first ==
'\\' && filename[1] ==
'\\')
793const gboolean dt_util_path_is_UNC(
const gchar *filename)
795 return filename[0] == G_DIR_SEPARATOR && filename[1] == G_DIR_SEPARATOR;
802 gchar *dirname = g_path_get_dirname(filename);
807 int last = strlen(dirname) - 1;
808 if(G_IS_DIR_SEPARATOR(dirname[last]))
809 dirname[last] =
'\0';
819 GFile *file = g_file_new_for_path(path);
820 GError *
error = NULL;
821 GFileInfo *info = g_file_query_info(file, G_FILE_ATTRIBUTE_STANDARD_NAME
"," G_FILE_ATTRIBUTE_TIME_MODIFIED,
822 G_FILE_QUERY_INFO_NONE, NULL, &
error);
826 g_object_unref(file);
830 const guint64 datetime = g_file_info_get_attribute_uint64(info, G_FILE_ATTRIBUTE_TIME_MODIFIED);
831 g_object_unref(file);
832 g_object_unref(info);
833 return g_date_time_new_from_unix_local(datetime);
842 if(text[0] == needle) count ++;
850 const struct lconv *currentLocalConv = localeconv();
851 const gchar loc_decimal_point = currentLocalConv->decimal_point[0];
852 const gchar *en_decimal_point =
".";
853 g_strdelimit(data, en_decimal_point, loc_decimal_point);
861 gchar *entry = g_strdup(text);
863 int len = strlen(prev);
866 gchar *next = g_strstr_len(prev, -1, separator);
869 const gchar c = next[0];
871 item = g_strdup(prev);
873 prev = next + strlen(separator);
875 list = g_list_prepend(list, item);
876 if(!len) list = g_list_prepend(list, g_strdup(
""));
880 item = g_strdup(prev);
882 list = g_list_prepend(list, item);
885 list = g_list_reverse(list);
894 if(exposuretime >= 1.0f)
896 if(nearbyintf(exposuretime) == exposuretime)
897 result = g_strdup_printf(
"%.0f\"", exposuretime);
899 result = g_strdup_printf(
"%.1f\"", exposuretime);
902 else if(exposuretime < 0.29f)
903 result = g_strdup_printf(
"1/%.0f", 1.0 / exposuretime);
906 else if(nearbyintf(1.0f / exposuretime) == 1.0f / exposuretime)
907 result = g_strdup_printf(
"1/%.0f", 1.0 / exposuretime);
910 else if(10 * nearbyintf(10.0f / exposuretime) == nearbyintf(100.0f / exposuretime))
911 result = g_strdup_printf(
"1/%.1f", 1.0 / exposuretime);
914 result = g_strdup_printf(
"%.1f\"", exposuretime);
921 if (filesize) *filesize = 0;
922 FILE *fd = g_fopen(filename,
"rb");
925 fseek(fd, 0, SEEK_END);
926 const size_t end = ftell(fd);
929 char *content = (
char *)malloc(
sizeof(
char) * end);
932 const size_t count = fread(content,
sizeof(
char), end, fd);
936 if (filesize) *filesize = end;
956 FILE *fin = g_fopen(sourcefile,
"rb");
957 FILE *fout = g_fopen(dst,
"wb");
961 char buffer[64 * 1024];
962 size_t bytes_read = fread(buffer,
sizeof(
char),
sizeof(buffer), fin);
964 while(bytes_read > 0)
966 if(fwrite(buffer,
sizeof(
char), bytes_read, fout) != bytes_read)
break;
967 bytes_read = fread(buffer,
sizeof(
char),
sizeof(buffer), fin);
979 gchar *sourcefile = g_build_filename(share, src, NULL);
988 #if LIBRSVG_CHECK_VERSION(2,52,0)
991 if(rsvg_handle_get_intrinsic_size_in_pixels(svg, &
width, &
height))
998#define VIEWPORT_SIZE 32767
999 const RsvgRectangle viewport = {
1002 .width = VIEWPORT_SIZE,
1003 .height = VIEWPORT_SIZE,
1006 RsvgRectangle rectangle;
1007 rsvg_handle_get_geometry_for_layer(svg, NULL, &viewport, NULL, &rectangle, NULL);
1008 dimension.width = lround(rectangle.width);
1009 dimension.height = lround(rectangle.height);
1012 rsvg_handle_get_dimensions(svg, &
dimension);
1020 #if LIBRSVG_CHECK_VERSION(2,52,0)
1021 RsvgRectangle viewport = {
1027 rsvg_handle_render_document(svg, cr, &viewport, NULL);
1029 rsvg_handle_render_cairo(svg, cr);
1037 const char *basename = strrchr(path,
'/');
1038 const char *windows_basename = strrchr(path,
'\\');
1040 basename = windows_basename;
1043 const char *dot = strrchr(basename ? basename : path,
'.');
1044 if(
IS_NULL_PTR(dot) || dot == (basename ? basename : path) || dot[1] ==
'\0')
return NULL;
1057 const size_t prefix1_length = extension1 - filename1;
1058 const size_t prefix2_length = extension2 - filename2;
1059 return prefix1_length == prefix2_length && !strncmp(filename1, filename2, prefix1_length);
1070 const size_t basename_length = extension1 - filename1 - 1;
1071 const size_t extension_length = strlen(extension2);
1072 char *output = g_try_malloc(basename_length + extension_length + 2);
1075 memcpy(output, filename1, basename_length);
1076 output[basename_length] =
'.';
1077 memcpy(output + basename_length + 1, extension2, extension_length + 1);
1084 gchar **split = g_strsplit(
string, search, -1);
1085 gchar *
res = g_strjoinv(replace, split);
1104 gchar **split = g_strsplit(path, G_DIR_SEPARATOR_S, -1);
1105 for(
int i = 0;
i < g_strv_length(split);
i++)
1106 g_strstrip(split[
i]);
1108 char* result = g_strjoinv(G_DIR_SEPARATOR_S, split);
static void error(char *msg)
static const float const float const float min
void dt_loc_get_datadir(char *datadir, size_t bufsize)
gchar * dt_loc_get_home_dir(const gchar *user)
const dt_collection_sort_t items[]
static gchar * g_realpath(const char *path)
float *const restrict const size_t k
#define IS_NULL_PTR(p)
C is way too permissive with !=, == and if(var) checks, which can mean too many things depending on w...
#define dt_free(ptr)
g_free() ptr and set it to NULL, skipping both if it is already NULL.
#define DT_PATH_MAX
Buffer size for a filesystem path anywhere in Ansel.
double dt_screen_ppd(void)
static cairo_surface_t * dt_cairo_image_surface_create_for_data(unsigned char *data, cairo_format_t format, int width, int height, int stride)
static const dt_aligned_pixel_simd_t sign
static const dt_aligned_pixel_simd_t value
static const char *const day[7]
gboolean dt_util_gps_elevation_to_number(const double r_1, const double r_2, char sign, double *result)
guint dt_util_string_count_char(const char *text, const char needle)
gchar * dt_util_str_replace(const gchar *string, const gchar *pattern, const gchar *substitute)
gchar * dt_util_longitude_str(float longitude)
gchar * dt_str_replace(const char *string, const char *search, const char *replace)
gboolean dt_has_same_path_basename(const char *filename1, const char *filename2)
Test whether two paths are byte-identical before their normal extensions.
void dt_util_str_to_loc_numbers_format(char *data)
#define OSD_COORDINATES_CHR_N
gchar * dt_cleanup_separators(gchar *string)
gchar * dt_util_elevation_str(float elevation)
gboolean dt_util_dir_exist(const char *dir)
dt_logo_season_t dt_util_get_logo_season(void)
RsvgDimensionData dt_get_svg_dimension(RsvgHandle *svg)
char * dt_read_file(const char *const filename, size_t *filesize)
static const char * OSD_ELEVATION_BSL
#define OSD_COORDINATES_CHR_S
cairo_surface_t * dt_util_get_logo_text(const float size)
static void easter(int Y, int *month, int *day)
GDateTime * dt_util_get_file_datetime(const char *const path)
GList * dt_util_str_to_glist(const gchar *separator, const gchar *text)
gboolean dt_util_test_image_file(const char *filename)
#define OSD_COORDINATES_CHR_W
int dt_util_streams_equal(FILE *first, FILE *second)
Compare the remaining bytes of two borrowed binary streams.
void dt_copy_file(const char *const sourcefile, const char *dst)
void dt_copy_resource_file(const char *src, const char *dst)
guint dt_util_str_occurence(const gchar *haystack, const gchar *needle)
double dt_util_gps_string_to_number(const gchar *input)
gchar * dt_util_path_get_dirname(const gchar *filename)
gboolean dt_util_is_dir_empty(const char *dirname)
#define OSD_COORDINATES_CHR_E
gchar * dt_util_normalize_path(const gchar *_input)
size_t dt_utf8_strlcpy(char *dest, const char *src, size_t n)
size_t safe_strlen(const char *str)
check if the string is empty or NULL before calling strlen()
char * dt_util_format_exposure(const float exposuretime)
gboolean dt_util_test_writable_dir(const char *path)
gboolean dt_util_gps_rationale_to_number(const double r0_1, const double r0_2, const double r1_1, const double r1_2, const double r2_1, const double r2_2, char sign, double *result)
gchar * dt_util_fix_path(const gchar *path)
const char * dt_util_path_get_extension(const char *path)
Locate a normal filename extension in a path basename.
gchar * dt_util_foo_to_utf8(const char *string)
gchar * dt_util_latitude_str(float latitude)
gchar * dt_util_remove_whitespace(const gchar *path)
void dt_render_svg(RsvgHandle *svg, cairo_t *cr, double width, double height, double offset_x, double offset_y)
static const char * OSD_ELEVATION_ASL
gchar * dt_util_glist_to_str(const gchar *separator, GList *items)
char * dt_copy_filename_extension(const char *filename1, const char *filename2)
Replace filename1's normal extension with filename2's normal extension.
static cairo_surface_t * _util_get_svg_img(gchar *logo, const float size)
gchar * dt_util_dstrcat(gchar *str, const gchar *format,...)
GList * dt_util_glist_uniq(GList *items)
cairo_surface_t * dt_util_get_logo(const float size)
@ DT_LOGO_SEASON_HALLOWEEN