Code

Fix drag & drop from swatches - patch by Adonis Papaderos
[inkscape.git] / src / io / sys.cpp
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
107 FILE *Inkscape::IO::fopen_utf8name( char const *utf8name, char const *mode )
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;
140 int Inkscape::IO::mkdir_utf8name( char const *utf8name )
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;
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)
188     return Glib::file_open_tmp(name_used, prefix);
191 bool Inkscape::IO::file_test( char const *utf8name, GFileTest test )
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;
220 bool Inkscape::IO::file_is_writable( char const *utf8name)
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;
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)
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     }
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)
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     }
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 )
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;
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)
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);
339 gchar* Inkscape::IO::sanitizeString( gchar const * str )
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;
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 :