2 /*
3 * System abstraction utility routines
4 *
5 * Authors:
6 * Jon A. Cruz <jon@joncruz.org>
7 *
8 * Copyright (C) 2004-2005 Authors
9 *
10 * Released under GNU GPL, read the file 'COPYING' for more information
11 */
14 #ifdef HAVE_CONFIG_H
15 # include "config.h"
16 #endif
18 #include <glib.h>
19 #include <glib/gstdio.h>
20 #include <glib/gutils.h>
21 #include <glibmm/fileutils.h>
22 #if GLIB_CHECK_VERSION(2,6,0)
23 #include <glib/gstdio.h>
24 #endif
25 #include <glibmm/ustring.h>
26 #include <gtk/gtkmessagedialog.h>
28 #include "preferences.h"
29 #include "sys.h"
31 #ifdef WIN32
32 // For now to get at is_os_wide().
33 #include "extension/internal/win32.h"
34 using Inkscape::Extension::Internal::PrintWin32;
35 #endif // WIN32
37 //#define INK_DUMP_FILENAME_CONV 1
38 #undef INK_DUMP_FILENAME_CONV
40 //#define INK_DUMP_FOPEN 1
41 #undef INK_DUMP_FOPEN
43 void dump_str(gchar const *str, gchar const *prefix);
44 void dump_ustr(Glib::ustring const &ustr);
46 extern guint update_in_progress;
49 #define DEBUG_MESSAGE(key, ...) \
50 {\
51 Inkscape::Preferences *prefs = Inkscape::Preferences::get(); \
52 gint dump = prefs->getBool("/options/bulia/" #key) ? 1 : 0;\
53 gint dumpD = prefs->getBool("/options/bulia/" #key"D") ? 1 : 0;\
54 gint dumpD2 = prefs->getBool("/options/bulia/" #key"D2") ? 1 : 0;\
55 dumpD &= ( (update_in_progress == 0) || dumpD2 );\
56 if ( dump )\
57 {\
58 g_message( __VA_ARGS__ );\
59 \
60 }\
61 if ( dumpD )\
62 {\
63 GtkWidget *dialog = gtk_message_dialog_new(NULL,\
64 GTK_DIALOG_DESTROY_WITH_PARENT, \
65 GTK_MESSAGE_INFO, \
66 GTK_BUTTONS_OK, \
67 __VA_ARGS__ \
68 );\
69 g_signal_connect_swapped(dialog, "response",\
70 G_CALLBACK(gtk_widget_destroy), \
71 dialog); \
72 gtk_widget_show_all( dialog );\
73 }\
74 }
79 void Inkscape::IO::dump_fopen_call( char const *utf8name, char const *id )
80 {
81 #ifdef INK_DUMP_FOPEN
82 Glib::ustring str;
83 for ( int i = 0; utf8name[i]; i++ )
84 {
85 if ( utf8name[i] == '\\' )
86 {
87 str += "\\\\";
88 }
89 else if ( (utf8name[i] >= 0x20) && ((0x0ff & utf8name[i]) <= 0x7f) )
90 {
91 str += utf8name[i];
92 }
93 else
94 {
95 gchar tmp[32];
96 g_snprintf( tmp, sizeof(tmp), "\\x%02x", (0x0ff & utf8name[i]) );
97 str += tmp;
98 }
99 }
100 g_message( "fopen call %s for [%s]", id, str.data() );
101 #else
102 (void)utf8name;
103 (void)id;
104 #endif
105 }
107 FILE *Inkscape::IO::fopen_utf8name( char const *utf8name, char const *mode )
108 {
109 static gint counter = 0;
110 FILE* fp = NULL;
112 DEBUG_MESSAGE( dumpOne, "entering fopen_utf8name( '%s', '%s' )[%d]", utf8name, mode, (counter++) );
114 #ifndef WIN32
115 DEBUG_MESSAGE( dumpOne, " STEP 0 ( '%s', '%s' )[%d]", utf8name, mode, (counter++) );
116 gchar *filename = g_filename_from_utf8( utf8name, -1, NULL, NULL, NULL );
117 if ( filename )
118 {
119 DEBUG_MESSAGE( dumpOne, " STEP 1 ( '%s', '%s' )[%d]", utf8name, mode, (counter++) );
120 fp = std::fopen(filename, mode);
121 DEBUG_MESSAGE( dumpOne, " STEP 2 ( '%s', '%s' )[%d]", utf8name, mode, (counter++) );
122 g_free(filename);
123 DEBUG_MESSAGE( dumpOne, " STEP 3 ( '%s', '%s' )[%d]", utf8name, mode, (counter++) );
124 filename = 0;
125 }
126 #else
127 Glib::ustring how( mode );
128 how.append("b");
129 DEBUG_MESSAGE( dumpOne, " calling is_os_wide() ( '%s', '%s' )[%d]", utf8name, mode, (counter++) );
131 fp = g_fopen(utf8name, how.c_str());
132 #endif
134 DEBUG_MESSAGE( dumpOne, "leaving fopen_utf8name( '%s', '%s' )[%d]", utf8name, mode, (counter++) );
136 return fp;
137 }
140 int Inkscape::IO::mkdir_utf8name( char const *utf8name )
141 {
142 static gint counter = 0;
143 int retval = -1;
145 DEBUG_MESSAGE( dumpMk, "entering mkdir_utf8name( '%s' )[%d]", utf8name, (counter++) );
147 #ifndef WIN32
148 DEBUG_MESSAGE( dumpMk, " STEP 0 ( '%s' )[%d]", utf8name, (counter++) );
149 gchar *filename = g_filename_from_utf8( utf8name, -1, NULL, NULL, NULL );
150 if ( filename )
151 {
152 DEBUG_MESSAGE( dumpMk, " STEP 1 ( '%s' )[%d]", utf8name, (counter++) );
153 retval = ::mkdir(filename, S_IRWXU | S_IRGRP | S_IXGRP);
154 DEBUG_MESSAGE( dumpMk, " STEP 2 ( '%s' )[%d]", utf8name, (counter++) );
155 g_free(filename);
156 DEBUG_MESSAGE( dumpMk, " STEP 3 ( '%s' )[%d]", utf8name, (counter++) );
157 filename = 0;
158 }
159 #else
160 DEBUG_MESSAGE( dumpMk, " calling is_os_wide() ( '%s' )[%d]", utf8name, (counter++) );
162 // Mode should be ingnored inside of glib on the way in
163 retval = g_mkdir( utf8name, 0 );
164 #endif
166 DEBUG_MESSAGE( dumpMk, "leaving mkdir_utf8name( '%s' )[%d]", utf8name, (counter++) );
168 return retval;
169 }
171 /*
172 * Wrapper around Glib::file_open_tmp().
173 * Returns a handle to the temp file.
174 * name_used contains the actual name used (a raw filename, not necessarily utf8).
175 *
176 * Returns:
177 * A file handle (as from open()) to the file opened for reading and writing.
178 * The file is opened in binary mode on platforms where there is a difference.
179 * The file handle should be closed with close().
180 *
181 * Note:
182 * On Windows Vista Glib::file_open_tmp fails with the current version of glibmm
183 * A special case is implemented for WIN32. This can be removed if the issue is fixed
184 * in future versions of glibmm
185 * */
186 int Inkscape::IO::file_open_tmp(std::string& name_used, const std::string& prefix)
187 {
188 return Glib::file_open_tmp(name_used, prefix);
189 }
191 bool Inkscape::IO::file_test( char const *utf8name, GFileTest test )
192 {
193 bool exists = false;
195 if ( utf8name ) {
196 gchar *filename = NULL;
197 if (utf8name && !g_utf8_validate(utf8name, -1, NULL)) {
198 /* FIXME: Trying to guess whether or not a filename is already in utf8 is unreliable.
199 If any callers pass non-utf8 data (e.g. using g_get_home_dir), then change caller to
200 use simple g_file_test. Then add g_return_val_if_fail(g_utf_validate(...), false)
201 to beginning of this function. */
202 filename = g_strdup(utf8name);
203 // Looks like g_get_home_dir isn't safe.
204 //g_warning("invalid UTF-8 detected internally. HUNT IT DOWN AND KILL IT!!!");
205 } else {
206 filename = g_filename_from_utf8 ( utf8name, -1, NULL, NULL, NULL );
207 }
208 if ( filename ) {
209 exists = g_file_test (filename, test);
210 g_free(filename);
211 filename = NULL;
212 } else {
213 g_warning( "Unable to convert filename in IO:file_test" );
214 }
215 }
217 return exists;
218 }
220 bool Inkscape::IO::file_is_writable( char const *utf8name)
221 {
222 bool success = true;
224 if ( utf8name) {
225 gchar *filename = NULL;
226 if (utf8name && !g_utf8_validate(utf8name, -1, NULL)) {
227 /* FIXME: Trying to guess whether or not a filename is already in utf8 is unreliable.
228 If any callers pass non-utf8 data (e.g. using g_get_home_dir), then change caller to
229 use simple g_file_test. Then add g_return_val_if_fail(g_utf_validate(...), false)
230 to beginning of this function. */
231 filename = g_strdup(utf8name);
232 // Looks like g_get_home_dir isn't safe.
233 //g_warning("invalid UTF-8 detected internally. HUNT IT DOWN AND KILL IT!!!");
234 } else {
235 filename = g_filename_from_utf8 ( utf8name, -1, NULL, NULL, NULL );
236 }
237 if ( filename ) {
238 struct stat st;
239 if(g_lstat (filename, &st) == 0) {
240 success = ((st.st_mode & S_IWRITE) != 0);
241 }
242 g_free(filename);
243 filename = NULL;
244 } else {
245 g_warning( "Unable to convert filename in IO:file_test" );
246 }
247 }
249 return success;
250 }
252 /** Wrapper around g_dir_open, but taking a utf8name as first argument. */
253 GDir *
254 Inkscape::IO::dir_open(gchar const *const utf8name, guint const flags, GError **const error)
255 {
256 gchar *const opsys_name = g_filename_from_utf8(utf8name, -1, NULL, NULL, error);
257 if (opsys_name) {
258 GDir *ret = g_dir_open(opsys_name, flags, error);
259 g_free(opsys_name);
260 return ret;
261 } else {
262 return NULL;
263 }
264 }
266 /**
267 * Like g_dir_read_name, but returns a utf8name (which must be freed, unlike g_dir_read_name).
268 *
269 * N.B. Skips over any dir entries that fail to convert to utf8.
270 */
271 gchar *
272 Inkscape::IO::dir_read_utf8name(GDir *dir)
273 {
274 for (;;) {
275 gchar const *const opsys_name = g_dir_read_name(dir);
276 if (!opsys_name) {
277 return NULL;
278 }
279 gchar *utf8_name = g_filename_to_utf8(opsys_name, -1, NULL, NULL, NULL);
280 if (utf8_name) {
281 return utf8_name;
282 }
283 }
284 }
287 gchar* Inkscape::IO::locale_to_utf8_fallback( const gchar *opsysstring,
288 gssize len,
289 gsize *bytes_read,
290 gsize *bytes_written,
291 GError **error )
292 {
293 gchar *result = NULL;
294 if ( opsysstring ) {
295 gchar *newFileName = g_locale_to_utf8( opsysstring, len, bytes_read, bytes_written, error );
296 if ( newFileName ) {
297 if ( !g_utf8_validate(newFileName, -1, NULL) ) {
298 g_warning( "input filename did not yield UTF-8" );
299 g_free( newFileName );
300 } else {
301 result = newFileName;
302 }
303 newFileName = 0;
304 } else if ( g_utf8_validate(opsysstring, -1, NULL) ) {
305 // This *might* be a case that we want
306 // g_warning( "input failed filename->utf8, fell back to original" );
307 // TODO handle cases when len >= 0
308 result = g_strdup( opsysstring );
309 } else {
310 gchar const *charset = 0;
311 g_get_charset(&charset);
312 g_warning( "input filename conversion failed for file with locale charset '%s'", charset );
313 }
314 }
315 return result;
316 }
318 void
319 Inkscape::IO::spawn_async_with_pipes( const std::string& working_directory,
320 const Glib::ArrayHandle<std::string>& argv,
321 Glib::SpawnFlags flags,
322 const sigc::slot<void>& child_setup,
323 Glib::Pid* child_pid,
324 int* standard_input,
325 int* standard_output,
326 int* standard_error)
327 {
328 Glib::spawn_async_with_pipes(working_directory,
329 argv,
330 flags,
331 child_setup,
332 child_pid,
333 standard_input,
334 standard_output,
335 standard_error);
336 }
339 gchar* Inkscape::IO::sanitizeString( gchar const * str )
340 {
341 gchar *result = NULL;
342 if ( str ) {
343 if ( g_utf8_validate(str, -1, NULL) ) {
344 result = g_strdup(str);
345 } else {
346 guchar scratch[8];
347 Glib::ustring buf;
348 guchar const *ptr = (guchar const*)str;
349 while ( *ptr )
350 {
351 if ( *ptr == '\\' )
352 {
353 buf.append("\\\\");
354 } else if ( *ptr < 0x80 ) {
355 buf += (char)(*ptr);
356 } else {
357 g_snprintf((gchar*)scratch, sizeof(scratch), "\\x%02x", *ptr);
358 buf.append((const char*)scratch);
359 }
360 ptr++;
361 }
362 result = g_strdup(buf.c_str());
363 }
364 }
365 return result;
366 }
368 /*
369 Local Variables:
370 mode:c++
371 c-file-style:"stroustrup"
372 c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
373 indent-tabs-mode:nil
374 fill-column:99
375 End:
376 */
377 // vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4 :