Ansel 0.0
A darktable fork - bloat + design vision
Loading...
Searching...
No Matches
ansel-lens-db-update/main.c
Go to the documentation of this file.
1/*
2 This file is part of Ansel,
3 Copyright (C) 2026 Aurélien PIERRE.
4
5 Ansel 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 3 of the License, or
8 (at your option) any later version.
9
10 Ansel 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 Ansel. If not, see <http://www.gnu.org/licenses/>.
17*/
18
51#include "common/paths.h" // DT_PATH_MAX
52#include "whereami.h"
53#include "lensserious_import.h"
54
55#include <glib.h>
56#include <glib/gstdio.h>
57#include <limits.h>
58#include <stdio.h>
59#include <stdlib.h>
60#include <string.h>
61
62#ifdef __APPLE__
63#include "osx/osx.h" // conditional-ok: dt_osx_prepare_environment() is called only
64 // from the matching __APPLE__ block in main()
65#endif
66
67#ifdef _WIN32
68// This header DEFINES wmain(), which -municode makes the entry point on Windows, and which
69// forwards to main() with the arguments converted to UTF-8. Without it the linker finds no
70// entry it recognises, falls back to the GUI-subsystem C runtime, and fails on an undefined
71// wWinMain. Every other app in src/apps includes it the same way.
72#include "win/main_wrapper.h" // conditional-ok: it defines the Windows entry point itself
73#endif
74
75static void usage(const char *progname)
76{
77 fprintf(stderr,
78 "usage: %s [options]\n"
79 "\n"
80 "Rebuilds Ansel's lens-correction database from the lensfun profiles installed\n"
81 "on THIS machine, including any you wrote yourself. The result is written to\n"
82 "your configuration directory, where Ansel looks before the one it shipped with.\n"
83 "\n"
84 "Close Ansel before running this: a running instance keeps its database open and\n"
85 "would not see the new file until restarted.\n"
86 "\n"
87 "options:\n"
88 " --xml-dir <dir> read exactly this directory of lensfun XML, instead of\n"
89 " letting lensfun search its four standard locations.\n"
90 " Skips your own profiles unless they are in <dir>.\n"
91 " --output <path> write here instead of <configdir>/lenses.db\n"
92 " --schema <path> the SQL schema; defaults to the installed one\n"
93 " --configdir <dir> use this configuration directory\n"
94 " --datadir <dir> use this data directory\n"
95 " --no-baseline do not start from the calibrations Ansel shipped with, so\n"
96 " the result holds only what lensfun finds on this machine\n"
97 " -h, --help this text\n"
98 "\n"
99 "To pull upstream's newest calibrations first, use lensfun's own updater:\n"
100 " lensfun-update-data\n"
101 "then run this to convert what it fetched.\n",
102 progname);
103}
104
105int main(int argc, char *argv[])
106{
107#ifdef __APPLE__
109#endif
110
111 const char *xml_dir = NULL;
112 const char *out_override = NULL;
113 const char *schema_override = NULL;
114 const char *configdir = NULL;
115 const char *datadir = NULL;
116 int no_baseline = 0;
117
118 for(int k = 1; k < argc; k++)
119 {
120 if(!strcmp(argv[k], "-h") || !strcmp(argv[k], "--help"))
121 {
122 usage(argv[0]);
123 return 0;
124 }
125
126 if(!strcmp(argv[k], "--no-baseline"))
127 {
128 no_baseline = 1;
129 continue;
130 }
131
132 /* Every option below takes a value, so a missing one is an error rather than a
133 * silently ignored flag -- the difference between "read your own profiles" and "read
134 * one directory" is exactly the kind of thing that must not be decided by a typo. */
135 const char *const opts[] = { "--xml-dir", "--output", "--schema", "--configdir", "--datadir" };
136 const char **const dests[] = { &xml_dir, &out_override, &schema_override, &configdir, &datadir };
137 int matched = 0;
138 for(size_t o = 0; o < sizeof(opts) / sizeof(*opts); o++)
139 {
140 if(strcmp(argv[k], opts[o])) continue;
141 if(k + 1 >= argc)
142 {
143 fprintf(stderr, "%s: %s needs a value\n", argv[0], opts[o]);
144 return 2;
145 }
146 *dests[o] = argv[++k];
147 matched = 1;
148 break;
149 }
150 if(matched) continue;
151
152 fprintf(stderr, "%s: unknown argument `%s'\n", argv[0], argv[k]);
153 usage(argv[0]);
154 return 2;
155 }
156
157 /* Resolves the same paths the application resolves, from the same code, so this writes
158 * where Ansel will actually look. Doing the lookup by hand here is how the two would
159 * drift the first time a packager moves something.
160 *
161 * Only the two directories this tool actually reads, though, rather than dt_loc_init()'s
162 * eight. Each initialiser ends in dt_check_opendir(), which exit()s the process when a
163 * directory is missing and could not be created -- and dt_loc_init_generic() creates with
164 * mode 0700, which a normal user cannot do under a system prefix. Asking for the module,
165 * locale, kernel, cache, share and temporary directories would make this tool die over
166 * any of the six it never opens. */
167 char *application_directory = NULL;
168 int dirname_length = 0;
169 const int length = wai_getExecutablePath(NULL, 0, &dirname_length);
170 if(length > 0)
171 {
172 application_directory = (char *)malloc(length + 1);
173 if(application_directory)
174 {
175 wai_getExecutablePath(application_directory, length, &dirname_length);
176 application_directory[dirname_length] = '\0';
177 }
178 }
179 dt_loc_init_datadir(application_directory, datadir);
181 free(application_directory);
182
183 char schema_path[DT_PATH_MAX] = { 0 };
184 if(schema_override)
185 g_strlcpy(schema_path, schema_override, sizeof(schema_path));
186 else
187 snprintf(schema_path, sizeof(schema_path), "%s/lenses-schema.sql", dt_loc_datadir());
188
189 if(!g_file_test(schema_path, G_FILE_TEST_IS_REGULAR))
190 {
191 fprintf(stderr,
192 "%s: no schema at `%s'.\n"
193 "This file is installed with Ansel; if it is missing, the installation is\n"
194 "incomplete. Pass --schema to point at one explicitly.\n",
195 argv[0], schema_path);
196 return 1;
197 }
198
199 char out_path[DT_PATH_MAX] = { 0 };
200 if(out_override)
201 g_strlcpy(out_path, out_override, sizeof(out_path));
202 else
203 snprintf(out_path, sizeof(out_path), "%s/lenses.db", dt_loc_configdir());
204
205 /* The calibrations Ansel shipped, loaded first so everything below merely overrides
206 * them. This is what makes the update additive rather than a replacement -- see
207 * ls_import_run(). */
208 char base_dir[DT_PATH_MAX] = { 0 };
209 const char *base = NULL;
210 if(!no_baseline)
211 {
212 snprintf(base_dir, sizeof(base_dir), "%s/lensfun-xml", dt_loc_datadir());
213 if(g_file_test(base_dir, G_FILE_TEST_IS_DIR))
214 base = base_dir;
215 else
216 fprintf(stderr,
217 "warning: no shipped calibrations at `%s'.\n"
218 "The result will hold ONLY what lensfun finds on this machine, which may be\n"
219 "far less than Ansel came with.\n",
220 base_dir);
221 }
222
223 if(xml_dir)
224 printf("Reading lensfun XML from `%s'.\n", xml_dir);
225 else
226 printf("Reading every lensfun profile this machine has: the system database, the\n"
227 "system and user update directories, and your own profiles in\n"
228 "~/.local/share/lensfun (which override the rest).\n");
229
230 /* Written aside and only then moved into place, because the result has to be JUDGED
231 * before it is allowed to shadow anything.
232 *
233 * lf_db_load() reads lensfun's own directories and knows nothing about the database
234 * Ansel ships. On a machine with no system-wide lensfun -- the normal case on Windows
235 * and macOS -- it can therefore come back with nothing but the handful of profiles the
236 * user wrote themselves. That file is not wrong, but installing it WOULD be: iop/lens.c
237 * prefers the configuration directory and never falls back once it opens something, so
238 * a three-lens database would silently replace the fifteen hundred that shipped. */
239 // g_strlcpy/g_strlcat rather than snprintf("%s.incoming"): both truncate safely, but the
240 // format version makes GCC warn that out_path can fill the buffer with no room for the
241 // suffix. dt_concat_path_file() is not usable here -- it inserts a separator, and this is
242 // a suffix on the same name.
243 char staged_path[DT_PATH_MAX] = { 0 };
244 g_strlcpy(staged_path, out_path, sizeof(staged_path));
245 g_strlcat(staged_path, ".incoming", sizeof(staged_path));
246
247 const int rc = ls_import_run(schema_path, staged_path, base, xml_dir);
248 if(rc)
249 {
250 g_unlink(staged_path);
251 fprintf(stderr, "%s: the database was NOT updated; the previous one is untouched.\n",
252 argv[0]);
253 return rc;
254 }
255
256 if(g_rename(staged_path, out_path) != 0)
257 {
258 fprintf(stderr, "%s: cannot move `%s' to `%s'; nothing was changed.\n",
259 argv[0], staged_path, out_path);
260 g_unlink(staged_path);
261 return 1;
262 }
263
264 printf("\nWrote `%s'.\n"
265 "Ansel reads this in preference to the database it shipped with, so the new\n"
266 "profiles are available next time you start it. Delete this file to go back to\n"
267 "the shipped one.\n",
268 out_path);
269 return 0;
270}
static void usage(const char *progname)
const char * dt_loc_datadir(void)
void dt_loc_init_datadir(const char *application_directory, const char *datadir)
const char * dt_loc_configdir(void)
void dt_loc_init_user_config_dir(const char *configdir)
float *const restrict const size_t k
void dt_osx_prepare_environment()
Definition osx.mm:213
#define DT_PATH_MAX
Buffer size for a filesystem path anywhere in Ansel.
Definition paths.h:57
int main()
Definition prova.c:47