~valavanisalex/ubuntu/precise/inkscape/fix-943984

« back to all changes in this revision

Viewing changes to inkscape-0.47pre1/src/extension/implementation/script.cpp

  • Committer: Bazaar Package Importer
  • Author(s): Bryce Harrington
  • Date: 2009-07-02 17:09:45 UTC
  • mfrom: (1.1.9 upstream)
  • Revision ID: james.westby@ubuntu.com-20090702170945-nn6d6zswovbwju1t
Tags: 0.47~pre1-0ubuntu1
* New upstream release.
  - Don't constrain maximization on small resolution devices (pre0)
    (LP: #348842)
  - Fixes segfault on startup (pre0)
    (LP: #391149)

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
/** \file
 
2
 * Code for handling extensions (i.e.\ scripts).
 
3
 */
 
4
/*
 
5
 * Authors:
 
6
 *   Bryce Harrington <bryce@osdl.org>
 
7
 *   Ted Gould <ted@gould.cx>
 
8
 *
 
9
 * Copyright (C) 2002-2005,2007 Authors
 
10
 *
 
11
 * Released under GNU GPL, read the file 'COPYING' for more information
 
12
 */
 
13
 
 
14
#define __INKSCAPE_EXTENSION_IMPLEMENTATION_SCRIPT_C__
 
15
 
 
16
#ifdef HAVE_CONFIG_H
 
17
# include <config.h>
 
18
#endif
 
19
 
 
20
#include <unistd.h>
 
21
 
 
22
#include <errno.h>
 
23
#include <gtkmm.h>
 
24
 
 
25
#include "ui/view/view.h"
 
26
#include "desktop-handles.h"
 
27
#include "desktop.h"
 
28
#include "selection.h"
 
29
#include "sp-namedview.h"
 
30
#include "io/sys.h"
 
31
#include "preferences.h"
 
32
#include "../system.h"
 
33
#include "extension/effect.h"
 
34
#include "extension/output.h"
 
35
#include "extension/input.h"
 
36
#include "extension/db.h"
 
37
#include "script.h"
 
38
#include "dialogs/dialog-events.h"
 
39
#include "application/application.h"
 
40
#include "xml/node.h"
 
41
#include "xml/attribute-record.h"
 
42
 
 
43
#include "util/glib-list-iterators.h"
 
44
 
 
45
 
 
46
 
 
47
#ifdef WIN32
 
48
#include <windows.h>
 
49
#include <sys/stat.h>
 
50
#include "registrytool.h"
 
51
#endif
 
52
 
 
53
 
 
54
 
 
55
/** This is the command buffer that gets allocated from the stack */
 
56
#define BUFSIZE (255)
 
57
 
 
58
 
 
59
 
 
60
/* Namespaces */
 
61
namespace Inkscape {
 
62
namespace Extension {
 
63
namespace Implementation {
 
64
 
 
65
/** \brief  Make GTK+ events continue to come through a little bit
 
66
        
 
67
        This just keeps coming the events through so that we'll make the GUI
 
68
        update and look pretty.
 
69
*/
 
70
void
 
71
Script::pump_events (void) {
 
72
    while( Gtk::Main::events_pending() )
 
73
        Gtk::Main::iteration();
 
74
    return;
 
75
}
 
76
 
 
77
 
 
78
/** \brief  A table of what interpreters to call for a given language
 
79
 
 
80
    This table is used to keep track of all the programs to execute a
 
81
    given script.  It also tracks the preference to use to overwrite
 
82
    the given interpreter to a custom one per user.
 
83
*/
 
84
Script::interpreter_t const Script::interpreterTab[] = {
 
85
        {"perl",   "perl-interpreter",   "perl"   },
 
86
#ifdef WIN32
 
87
        {"python", "python-interpreter", "pythonw" },
 
88
#else
 
89
        {"python", "python-interpreter", "python" },
 
90
#endif
 
91
        {"ruby",   "ruby-interpreter",   "ruby"   },
 
92
        {"shell",  "shell-interpreter",  "sh"     },
 
93
        { NULL,    NULL,                  NULL    }
 
94
};
 
95
 
 
96
 
 
97
 
 
98
/** \brief Look up an interpreter name, and translate to something that
 
99
           is executable
 
100
    \param interpNameArg  The name of the interpreter that we're looking
 
101
                              for, should be an entry in interpreterTab
 
102
*/
 
103
Glib::ustring
 
104
Script::resolveInterpreterExecutable(const Glib::ustring &interpNameArg)
 
105
{
 
106
 
 
107
    Glib::ustring interpName = interpNameArg;
 
108
 
 
109
    interpreter_t const *interp;
 
110
    bool foundInterp = false;
 
111
    for (interp =  interpreterTab ; interp->identity ; interp++ ){
 
112
        if (interpName == interp->identity) {
 
113
            foundInterp = true;
 
114
            break;
 
115
        }
 
116
    }
 
117
 
 
118
    // Do we have a supported interpreter type?
 
119
    if (!foundInterp)
 
120
        return "";
 
121
    interpName = interp->defaultval;
 
122
 
 
123
    // 1.  Check preferences
 
124
    Inkscape::Preferences *prefs = Inkscape::Preferences::get();
 
125
    Glib::ustring prefInterp = prefs->getString("/extensions/" + Glib::ustring(interp->prefstring));
 
126
 
 
127
    if (!prefInterp.empty()) {
 
128
        interpName = prefInterp;
 
129
        return interpName;
 
130
    }
 
131
 
 
132
#ifdef WIN32
 
133
 
 
134
    // 2.  Windows.  Try looking relative to inkscape.exe
 
135
    RegistryTool rt;
 
136
    Glib::ustring fullPath;
 
137
    Glib::ustring path;
 
138
    Glib::ustring exeName;
 
139
    if (rt.getExeInfo(fullPath, path, exeName)) {
 
140
        Glib::ustring interpPath = path;
 
141
        interpPath.append("\\");
 
142
        interpPath.append(interpNameArg);
 
143
        interpPath.append("\\");
 
144
        interpPath.append(interpName);
 
145
        interpPath.append(".exe");
 
146
        struct stat finfo;
 
147
        if (stat(interpPath .c_str(), &finfo) ==0) {
 
148
            g_message("Found local interpreter, '%s',  Size: %d",
 
149
                      interpPath .c_str(),
 
150
                      (int)finfo.st_size);
 
151
            return interpPath;
 
152
        }
 
153
    }
 
154
 
 
155
    // 3. Try searching the path
 
156
    char szExePath[MAX_PATH];
 
157
    char szCurrentDir[MAX_PATH];
 
158
    GetCurrentDirectory(sizeof(szCurrentDir), szCurrentDir);
 
159
    unsigned int ret = (unsigned int)FindExecutable(
 
160
                  interpName.c_str(), szCurrentDir, szExePath);
 
161
    if (ret > 32) {
 
162
        interpName = szExePath;
 
163
        return interpName;
 
164
    }
 
165
 
 
166
#endif // win32
 
167
 
 
168
 
 
169
    return interpName;
 
170
}
 
171
 
 
172
/** \brief     This function creates a script object and sets up the
 
173
               variables.
 
174
    \return    A script object
 
175
 
 
176
   This function just sets the command to NULL.  It should get built
 
177
   officially in the load function.  This allows for less allocation
 
178
   of memory in the unloaded state.
 
179
*/
 
180
Script::Script() :
 
181
    Implementation()
 
182
{
 
183
}
 
184
 
 
185
/**
 
186
 *   brief     Destructor
 
187
 */
 
188
Script::~Script()
 
189
{
 
190
}
 
191
 
 
192
 
 
193
 
 
194
/**
 
195
    \return    A string with the complete string with the relative directory expanded
 
196
    \brief     This function takes in a Repr that contains a reldir entry
 
197
               and returns that data with the relative directory expanded.
 
198
               Mostly it is here so that relative directories all get used
 
199
               the same way.
 
200
    \param     reprin   The Inkscape::XML::Node with the reldir in it.
 
201
 
 
202
    Basically this function looks at an attribute of the Repr, and makes
 
203
    a decision based on that.  Currently, it is only working with the
 
204
    'extensions' relative directory, but there will be more of them.
 
205
    One thing to notice is that this function always returns an allocated
 
206
    string.  This means that the caller of this function can always
 
207
    free what they are given (and should do it too!).
 
208
*/
 
209
Glib::ustring
 
210
Script::solve_reldir(Inkscape::XML::Node *reprin) {
 
211
 
 
212
    gchar const *s = reprin->attribute("reldir");
 
213
 
 
214
    if (!s) {
 
215
        Glib::ustring str = sp_repr_children(reprin)->content();
 
216
        return str;
 
217
    }
 
218
 
 
219
    Glib::ustring reldir = s;
 
220
 
 
221
    if (reldir == "extensions") {
 
222
 
 
223
        for (unsigned int i=0;
 
224
            i < Inkscape::Extension::Extension::search_path.size();
 
225
            i++) {
 
226
 
 
227
            gchar * fname = g_build_filename(
 
228
               Inkscape::Extension::Extension::search_path[i],
 
229
               sp_repr_children(reprin)->content(),
 
230
               NULL);
 
231
            Glib::ustring filename = fname;
 
232
            g_free(fname);
 
233
 
 
234
            if ( Inkscape::IO::file_test(filename.c_str(), G_FILE_TEST_EXISTS) )
 
235
                return filename;
 
236
 
 
237
        }
 
238
    } else {
 
239
        Glib::ustring str = sp_repr_children(reprin)->content();
 
240
        return str;
 
241
    }
 
242
 
 
243
    return "";
 
244
}
 
245
 
 
246
 
 
247
 
 
248
/**
 
249
    \return   Whether the command given exists, including in the path
 
250
    \brief    This function is used to find out if something exists for
 
251
              the check command.  It can look in the path if required.
 
252
    \param    command   The command or file that should be looked for
 
253
 
 
254
    The first thing that this function does is check to see if the
 
255
    incoming file name has a directory delimiter in it.  This would
 
256
    mean that it wants to control the directories, and should be
 
257
    used directly.
 
258
 
 
259
    If not, the path is used.  Each entry in the path is stepped through,
 
260
    attached to the string, and then tested.  If the file is found
 
261
    then a TRUE is returned.  If we get all the way through the path
 
262
    then a FALSE is returned, the command could not be found.
 
263
*/
 
264
bool
 
265
Script::check_existance(const Glib::ustring &command)
 
266
{
 
267
 
 
268
    // Check the simple case first
 
269
    if (command.size() == 0) {
 
270
        return false;
 
271
    }
 
272
 
 
273
    //Don't search when it contains a slash. */
 
274
    if (command.find(G_DIR_SEPARATOR) != command.npos) {
 
275
        if (Inkscape::IO::file_test(command.c_str(), G_FILE_TEST_EXISTS))
 
276
            return true;
 
277
        else
 
278
            return false;
 
279
    }
 
280
 
 
281
 
 
282
    Glib::ustring path;
 
283
    gchar *s = (gchar *) g_getenv("PATH");
 
284
    if (s)
 
285
        path = s;
 
286
    else
 
287
       /* There is no `PATH' in the environment.
 
288
           The default search path is the current directory */
 
289
        path = G_SEARCHPATH_SEPARATOR_S;
 
290
 
 
291
    std::string::size_type pos  = 0;
 
292
    std::string::size_type pos2 = 0;
 
293
    while ( pos < path.size() ) {
 
294
 
 
295
        Glib::ustring localPath;
 
296
 
 
297
        pos2 = path.find(G_SEARCHPATH_SEPARATOR, pos);
 
298
        if (pos2 == path.npos) {
 
299
            localPath = path.substr(pos);
 
300
            pos = path.size();
 
301
        } else {
 
302
            localPath = path.substr(pos, pos2-pos);
 
303
            pos = pos2+1;
 
304
        }
 
305
 
 
306
        //printf("### %s\n", localPath.c_str());
 
307
        Glib::ustring candidatePath =
 
308
                      Glib::build_filename(localPath, command);
 
309
 
 
310
        if (Inkscape::IO::file_test(candidatePath .c_str(),
 
311
                      G_FILE_TEST_EXISTS)) {
 
312
            return true;
 
313
        }
 
314
 
 
315
    }
 
316
 
 
317
    return false;
 
318
}
 
319
 
 
320
 
 
321
 
 
322
 
 
323
 
 
324
/**
 
325
    \return   none
 
326
    \brief    This function 'loads' an extention, basically it determines
 
327
              the full command for the extention and stores that.
 
328
    \param    module  The extention to be loaded.
 
329
 
 
330
    The most difficult part about this function is finding the actual
 
331
    command through all of the Reprs.  Basically it is hidden down a
 
332
    couple of layers, and so the code has to move down too.  When
 
333
    the command is actually found, it has its relative directory
 
334
    solved.
 
335
 
 
336
    At that point all of the loops are exited, and there is an
 
337
    if statement to make sure they didn't exit because of not finding
 
338
    the command.  If that's the case, the extention doesn't get loaded
 
339
    and should error out at a higher level.
 
340
*/
 
341
 
 
342
bool
 
343
Script::load(Inkscape::Extension::Extension *module)
 
344
{
 
345
    if (module->loaded())
 
346
        return true;
 
347
 
 
348
    helper_extension = "";
 
349
 
 
350
    /* This should probably check to find the executable... */
 
351
    Inkscape::XML::Node *child_repr = sp_repr_children(module->get_repr());
 
352
    while (child_repr != NULL) {
 
353
        if (!strcmp(child_repr->name(), INKSCAPE_EXTENSION_NS "script")) {
 
354
            child_repr = sp_repr_children(child_repr);
 
355
            while (child_repr != NULL) {
 
356
                if (!strcmp(child_repr->name(), INKSCAPE_EXTENSION_NS "command")) {
 
357
                    const gchar *interpretstr = child_repr->attribute("interpreter");
 
358
                    if (interpretstr != NULL) {
 
359
                        Glib::ustring interpString =
 
360
                            resolveInterpreterExecutable(interpretstr);
 
361
                        //g_message("Found: %s and %s",interpString.c_str(),interpretstr);
 
362
                        command.insert(command.end(), interpretstr);
 
363
                    }
 
364
                    Glib::ustring tmp = "\"";
 
365
                    tmp += solve_reldir(child_repr);
 
366
                    tmp += "\"";
 
367
 
 
368
                    command.insert(command.end(), tmp);
 
369
                }
 
370
                if (!strcmp(child_repr->name(), INKSCAPE_EXTENSION_NS "helper_extension")) {
 
371
                    helper_extension = sp_repr_children(child_repr)->content();
 
372
                }
 
373
                child_repr = sp_repr_next(child_repr);
 
374
            }
 
375
 
 
376
            break;
 
377
        }
 
378
        child_repr = sp_repr_next(child_repr);
 
379
    }
 
380
 
 
381
    //g_return_val_if_fail(command.length() > 0, false);
 
382
 
 
383
    return true;
 
384
}
 
385
 
 
386
 
 
387
/**
 
388
    \return   None.
 
389
    \brief    Unload this puppy!
 
390
    \param    module  Extension to be unloaded.
 
391
 
 
392
    This function just sets the module to unloaded.  It free's the
 
393
    command if it has been allocated.
 
394
*/
 
395
void
 
396
Script::unload(Inkscape::Extension::Extension */*module*/)
 
397
{
 
398
    command.clear();
 
399
    helper_extension = "";
 
400
}
 
401
 
 
402
 
 
403
 
 
404
 
 
405
/**
 
406
    \return   Whether the check passed or not
 
407
    \brief    Check every dependency that was given to make sure we should keep this extension
 
408
    \param    module  The Extension in question
 
409
 
 
410
*/
 
411
bool
 
412
Script::check(Inkscape::Extension::Extension *module)
 
413
{
 
414
        int script_count = 0;
 
415
    Inkscape::XML::Node *child_repr = sp_repr_children(module->get_repr());
 
416
    while (child_repr != NULL) {
 
417
        if (!strcmp(child_repr->name(), INKSCAPE_EXTENSION_NS "script")) {
 
418
                        script_count++;
 
419
            child_repr = sp_repr_children(child_repr);
 
420
            while (child_repr != NULL) {
 
421
                if (!strcmp(child_repr->name(), INKSCAPE_EXTENSION_NS "check")) {
 
422
                    Glib::ustring command_text = solve_reldir(child_repr);
 
423
                    if (command_text.size() > 0) {
 
424
                        /* I've got the command */
 
425
                        bool existance = check_existance(command_text);
 
426
                        if (!existance)
 
427
                            return false;
 
428
                    }
 
429
                }
 
430
 
 
431
                if (!strcmp(child_repr->name(), INKSCAPE_EXTENSION_NS "helper_extension")) {
 
432
                    gchar const *helper = sp_repr_children(child_repr)->content();
 
433
                    if (Inkscape::Extension::db.get(helper) == NULL) {
 
434
                        return false;
 
435
                    }
 
436
                }
 
437
 
 
438
                child_repr = sp_repr_next(child_repr);
 
439
            }
 
440
 
 
441
            break;
 
442
        }
 
443
        child_repr = sp_repr_next(child_repr);
 
444
    }
 
445
 
 
446
        if (script_count == 0) {
 
447
                return false;
 
448
        }
 
449
 
 
450
    return true;
 
451
}
 
452
 
 
453
class ScriptDocCache : public ImplementationDocumentCache {
 
454
    friend class Script;
 
455
protected:
 
456
    std::string _filename;
 
457
    int _tempfd;
 
458
public:
 
459
    ScriptDocCache (Inkscape::UI::View::View * view);
 
460
    ~ScriptDocCache ( );
 
461
};
 
462
 
 
463
ScriptDocCache::ScriptDocCache (Inkscape::UI::View::View * view) :
 
464
    ImplementationDocumentCache(view),
 
465
    _filename(""),
 
466
    _tempfd(0)
 
467
{
 
468
    try {
 
469
        _tempfd = Inkscape::IO::file_open_tmp(_filename, "ink_ext_XXXXXX.svg");
 
470
    } catch (...) {
 
471
        /// \todo Popup dialog here
 
472
        return;
 
473
    }
 
474
 
 
475
    SPDesktop *desktop = (SPDesktop *) view;
 
476
    sp_namedview_document_from_window(desktop);
 
477
 
 
478
    Inkscape::Extension::save(
 
479
              Inkscape::Extension::db.get(SP_MODULE_KEY_OUTPUT_SVG_INKSCAPE),
 
480
              view->doc(), _filename.c_str(), false, false, false);
 
481
 
 
482
    return;
 
483
}
 
484
 
 
485
ScriptDocCache::~ScriptDocCache ( )
 
486
{
 
487
    close(_tempfd);
 
488
    unlink(_filename.c_str());
 
489
}
 
490
 
 
491
ImplementationDocumentCache *
 
492
Script::newDocCache( Inkscape::Extension::Extension * /*ext*/, Inkscape::UI::View::View * view ) {
 
493
    return new ScriptDocCache(view);
 
494
}
 
495
 
 
496
 
 
497
/**
 
498
    \return   A dialog for preferences
 
499
    \brief    A stub funtion right now
 
500
    \param    module    Module who's preferences need getting
 
501
    \param    filename  Hey, the file you're getting might be important
 
502
 
 
503
    This function should really do something, right now it doesn't.
 
504
*/
 
505
Gtk::Widget *
 
506
Script::prefs_input(Inkscape::Extension::Input *module,
 
507
                    const gchar */*filename*/)
 
508
{
 
509
    return module->autogui(NULL, NULL);
 
510
}
 
511
 
 
512
 
 
513
 
 
514
/**
 
515
    \return   A dialog for preferences
 
516
    \brief    A stub funtion right now
 
517
    \param    module    Module whose preferences need getting
 
518
 
 
519
    This function should really do something, right now it doesn't.
 
520
*/
 
521
Gtk::Widget *
 
522
Script::prefs_output(Inkscape::Extension::Output *module)
 
523
{
 
524
    return module->autogui(NULL, NULL);
 
525
}
 
526
 
 
527
/**
 
528
    \return  A new document that has been opened
 
529
    \brief   This function uses a filename that is put in, and calls
 
530
             the extension's command to create an SVG file which is
 
531
             returned.
 
532
    \param   module   Extension to use.
 
533
    \param   filename File to open.
 
534
 
 
535
    First things first, this function needs a temporary file name.  To
 
536
    create on of those the function g_file_open_tmp is used with
 
537
    the header of ink_ext_.
 
538
 
 
539
    The extension is then executed using the 'execute' function
 
540
    with the filname coming in, and the temporary filename.  After
 
541
    That executing, the SVG should be in the temporary file.
 
542
 
 
543
    Finally, the temporary file is opened using the SVG input module and
 
544
    a document is returned.  That document has its filename set to
 
545
    the incoming filename (so that it's not the temporary filename).
 
546
    That document is then returned from this function.
 
547
*/
 
548
SPDocument *
 
549
Script::open(Inkscape::Extension::Input *module,
 
550
             const gchar *filenameArg)
 
551
{
 
552
    std::list<std::string> params;
 
553
    module->paramListString(params);
 
554
 
 
555
    std::string tempfilename_out;
 
556
    int tempfd_out = 0;
 
557
    try {
 
558
        tempfd_out = Inkscape::IO::file_open_tmp(tempfilename_out, "ink_ext_XXXXXX.svg");
 
559
    } catch (...) {
 
560
        /// \todo Popup dialog here
 
561
        return NULL;
 
562
    }
 
563
 
 
564
    std::string lfilename = Glib::filename_from_utf8(filenameArg);
 
565
 
 
566
    file_listener fileout;
 
567
    int data_read = execute(command, params, lfilename, fileout);
 
568
    fileout.toFile(tempfilename_out);
 
569
 
 
570
    SPDocument * mydoc = NULL;
 
571
    if (data_read > 10) {
 
572
        if (helper_extension.size()==0) {
 
573
            mydoc = Inkscape::Extension::open(
 
574
                  Inkscape::Extension::db.get(SP_MODULE_KEY_INPUT_SVG),
 
575
                  tempfilename_out.c_str());
 
576
        } else {
 
577
            mydoc = Inkscape::Extension::open(
 
578
                  Inkscape::Extension::db.get(helper_extension.c_str()),
 
579
                  tempfilename_out.c_str());
 
580
        }
 
581
    } // data_read
 
582
 
 
583
    if (mydoc != NULL) {
 
584
        g_free(mydoc->base);
 
585
        mydoc->base = NULL;
 
586
        sp_document_change_uri_and_hrefs(mydoc, filenameArg);
 
587
    }
 
588
 
 
589
    // make sure we don't leak file descriptors from g_file_open_tmp
 
590
    close(tempfd_out);
 
591
 
 
592
    unlink(tempfilename_out.c_str());
 
593
 
 
594
    return mydoc;
 
595
} // open
 
596
 
 
597
 
 
598
 
 
599
/**
 
600
    \return   none
 
601
    \brief    This function uses an extention to save a document.  It first
 
602
              creates an SVG file of the document, and then runs it through
 
603
              the script.
 
604
    \param    module    Extention to be used
 
605
    \param    doc       Document to be saved
 
606
    \param    filename  The name to save the final file as
 
607
 
 
608
    Well, at some point people need to save - it is really what makes
 
609
    the entire application useful.  And, it is possible that someone
 
610
    would want to use an extetion for this, so we need a function to
 
611
    do that eh?
 
612
 
 
613
    First things first, the document is saved to a temporary file that
 
614
    is an SVG file.  To get the temporary filename g_file_open_tmp is used with
 
615
    ink_ext_ as a prefix.  Don't worry, this file gets deleted at the
 
616
    end of the function.
 
617
 
 
618
    After we have the SVG file, then extention_execute is called with
 
619
    the temporary file name and the final output filename.  This should
 
620
    put the output of the script into the final output file.  We then
 
621
    delete the temporary file.
 
622
*/
 
623
void
 
624
Script::save(Inkscape::Extension::Output *module,
 
625
             SPDocument *doc,
 
626
             const gchar *filenameArg)
 
627
{
 
628
    std::list<std::string> params;
 
629
    module->paramListString(params);
 
630
 
 
631
    std::string tempfilename_in;
 
632
    int tempfd_in = 0;
 
633
    try {
 
634
        tempfd_in = Inkscape::IO::file_open_tmp(tempfilename_in, "ink_ext_XXXXXX.svg");
 
635
    } catch (...) {
 
636
        /// \todo Popup dialog here
 
637
        return;
 
638
    }
 
639
 
 
640
    if (helper_extension.size() == 0) {
 
641
        Inkscape::Extension::save(
 
642
                   Inkscape::Extension::db.get(SP_MODULE_KEY_OUTPUT_SVG_INKSCAPE),
 
643
                   doc, tempfilename_in.c_str(), false, false, false);
 
644
    } else {
 
645
        Inkscape::Extension::save(
 
646
                   Inkscape::Extension::db.get(helper_extension.c_str()),
 
647
                   doc, tempfilename_in.c_str(), false, false, false);
 
648
    }
 
649
 
 
650
 
 
651
    file_listener fileout;
 
652
    execute(command, params, tempfilename_in, fileout);
 
653
 
 
654
    std::string lfilename = Glib::filename_from_utf8(filenameArg);
 
655
    fileout.toFile(lfilename);
 
656
 
 
657
    // make sure we don't leak file descriptors from g_file_open_tmp
 
658
    close(tempfd_in);
 
659
    // FIXME: convert to utf8 (from "filename encoding") and unlink_utf8name
 
660
    unlink(tempfilename_in.c_str());
 
661
 
 
662
    return;
 
663
}
 
664
 
 
665
 
 
666
 
 
667
/**
 
668
    \return    none
 
669
    \brief     This function uses an extention as a effect on a document.
 
670
    \param     module   Extention to effect with.
 
671
    \param     doc      Document to run through the effect.
 
672
 
 
673
    This function is a little bit trickier than the previous two.  It
 
674
    needs two temporary files to get it's work done.  Both of these
 
675
    files have random names created for them using the g_file_open_temp function
 
676
    with the ink_ext_ prefix in the temporary directory.  Like the other
 
677
    functions, the temporary files are deleted at the end.
 
678
 
 
679
    To save/load the two temporary documents (both are SVG) the internal
 
680
    modules for SVG load and save are used.  They are both used through
 
681
    the module system function by passing their keys into the functions.
 
682
 
 
683
    The command itself is built a little bit differently than in other
 
684
    functions because the effect support selections.  So on the command
 
685
    line a list of all the ids that are selected is included.  Currently,
 
686
    this only works for a single selected object, but there will be more.
 
687
    The command string is filled with the data, and then after the execution
 
688
    it is freed.
 
689
 
 
690
    The execute function is used at the core of this function
 
691
    to execute the Script on the two SVG documents (actually only one
 
692
    exists at the time, the other is created by that script).  At that
 
693
    point both should be full, and the second one is loaded.
 
694
*/
 
695
void
 
696
Script::effect(Inkscape::Extension::Effect *module,
 
697
               Inkscape::UI::View::View *doc,
 
698
               ImplementationDocumentCache * docCache)
 
699
{
 
700
    if (docCache == NULL) {
 
701
        docCache = newDocCache(module, doc);
 
702
    }
 
703
    ScriptDocCache * dc = dynamic_cast<ScriptDocCache *>(docCache);
 
704
    if (dc == NULL) {
 
705
        printf("TOO BAD TO LIVE!!!");
 
706
        exit(1);
 
707
    }
 
708
 
 
709
    SPDesktop *desktop = (SPDesktop *)doc;
 
710
    sp_namedview_document_from_window(desktop);
 
711
 
 
712
    gchar * orig_output_extension = g_strdup(sp_document_repr_root(desktop->doc())->attribute("inkscape:output_extension"));
 
713
 
 
714
    std::list<std::string> params;
 
715
    module->paramListString(params);
 
716
 
 
717
    if (module->no_doc) {
 
718
        // this is a no-doc extension, e.g. a Help menu command;
 
719
        // just run the command without any files, ignoring errors
 
720
 
 
721
        Glib::ustring empty;
 
722
        file_listener outfile;
 
723
        execute(command, params, empty, outfile);
 
724
 
 
725
        return;
 
726
    }
 
727
 
 
728
    std::string tempfilename_out;
 
729
    int tempfd_out = 0;
 
730
    try {
 
731
        tempfd_out = Inkscape::IO::file_open_tmp(tempfilename_out, "ink_ext_XXXXXX.svg");
 
732
    } catch (...) {
 
733
        /// \todo Popup dialog here
 
734
        return;
 
735
    }
 
736
 
 
737
    if (desktop != NULL) {
 
738
        Inkscape::Util::GSListConstIterator<SPItem *> selected =
 
739
             sp_desktop_selection(desktop)->itemList();
 
740
        while ( selected != NULL ) {
 
741
            Glib::ustring selected_id;
 
742
            selected_id += "--id=";
 
743
            selected_id += SP_OBJECT_ID(*selected);
 
744
            params.insert(params.begin(), selected_id);
 
745
            ++selected;
 
746
        }
 
747
    }
 
748
 
 
749
    file_listener fileout;
 
750
    int data_read = execute(command, params, dc->_filename, fileout);
 
751
    fileout.toFile(tempfilename_out);
 
752
 
 
753
    pump_events();
 
754
 
 
755
    SPDocument * mydoc = NULL;
 
756
    if (data_read > 10) {
 
757
        mydoc = Inkscape::Extension::open(
 
758
              Inkscape::Extension::db.get(SP_MODULE_KEY_INPUT_SVG),
 
759
              tempfilename_out.c_str());
 
760
    } // data_read
 
761
 
 
762
    pump_events();
 
763
 
 
764
    // make sure we don't leak file descriptors from g_file_open_tmp
 
765
    close(tempfd_out);
 
766
 
 
767
    // FIXME: convert to utf8 (from "filename encoding") and unlink_utf8name
 
768
    unlink(tempfilename_out.c_str());
 
769
 
 
770
    /* Do something with mydoc.... */
 
771
    if (mydoc) {
 
772
        doc->doc()->emitReconstructionStart();
 
773
        copy_doc(doc->doc()->rroot, mydoc->rroot);
 
774
        doc->doc()->emitReconstructionFinish();
 
775
        mydoc->release();
 
776
        sp_namedview_update_layers_from_document(desktop);
 
777
 
 
778
        sp_document_repr_root(desktop->doc())->setAttribute("inkscape:output_extension", orig_output_extension);
 
779
    }
 
780
    g_free(orig_output_extension);
 
781
 
 
782
    return;
 
783
}
 
784
 
 
785
 
 
786
 
 
787
/**
 
788
    \brief  A function to take all the svg elements from one document
 
789
            and put them in another.
 
790
    \param  oldroot  The root node of the document to be replaced
 
791
    \param  newroot  The root node of the document to replace it with
 
792
 
 
793
    This function first deletes all of the data in the old document.  It
 
794
    does this by creating a list of what needs to be deleted, and then
 
795
    goes through the list.  This two pass approach removes issues with
 
796
    the list being change while parsing through it.  Lots of nasty bugs.
 
797
 
 
798
    Then, it goes through the new document, duplicating all of the
 
799
    elements and putting them into the old document.  The copy
 
800
    is then complete.
 
801
*/
 
802
void
 
803
Script::copy_doc (Inkscape::XML::Node * oldroot, Inkscape::XML::Node * newroot)
 
804
{
 
805
    std::vector<Inkscape::XML::Node *> delete_list;
 
806
    Inkscape::XML::Node * oldroot_namedview = NULL;
 
807
 
 
808
    for (Inkscape::XML::Node * child = oldroot->firstChild();
 
809
            child != NULL;
 
810
            child = child->next()) {
 
811
        if (!strcmp("sodipodi:namedview", child->name())) {
 
812
            oldroot_namedview = child;
 
813
            for (Inkscape::XML::Node * oldroot_namedview_child = child->firstChild();
 
814
                    oldroot_namedview_child != NULL;
 
815
                    oldroot_namedview_child = oldroot_namedview_child->next()) {
 
816
                delete_list.push_back(oldroot_namedview_child);
 
817
            }
 
818
        } else {
 
819
            delete_list.push_back(child);
 
820
        }
 
821
    }
 
822
    for (unsigned int i = 0; i < delete_list.size(); i++)
 
823
        sp_repr_unparent(delete_list[i]);
 
824
 
 
825
    for (Inkscape::XML::Node * child = newroot->firstChild();
 
826
            child != NULL;
 
827
            child = child->next()) {
 
828
        if (!strcmp("sodipodi:namedview", child->name())) {
 
829
            if (oldroot_namedview != NULL) {
 
830
                for (Inkscape::XML::Node * newroot_namedview_child = child->firstChild();
 
831
                        newroot_namedview_child != NULL;
 
832
                        newroot_namedview_child = newroot_namedview_child->next()) {
 
833
                    oldroot_namedview->appendChild(newroot_namedview_child->duplicate(oldroot->document()));
 
834
                }
 
835
            }
 
836
        } else {
 
837
            oldroot->appendChild(child->duplicate(oldroot->document()));
 
838
        }
 
839
    }
 
840
 
 
841
    {
 
842
        using Inkscape::Util::List;
 
843
        using Inkscape::XML::AttributeRecord;        
 
844
        std::vector<gchar const *> attribs;
 
845
 
 
846
        // Make a list of all attributes of the old root node.
 
847
        for (List<AttributeRecord const> iter = oldroot->attributeList(); iter; ++iter) {
 
848
            attribs.push_back(g_quark_to_string(iter->key));
 
849
        }
 
850
 
 
851
        // Delete the attributes of the old root nodes.
 
852
        for (std::vector<gchar const *>::const_iterator it = attribs.begin(); it != attribs.end(); it++)
 
853
            oldroot->setAttribute(*it, NULL);
 
854
 
 
855
        // Set the new attributes.
 
856
        for (List<AttributeRecord const> iter = newroot->attributeList(); iter; ++iter) {
 
857
            gchar const *name = g_quark_to_string(iter->key);
 
858
            oldroot->setAttribute(name, newroot->attribute(name));
 
859
        }
 
860
    }
 
861
 
 
862
    /** \todo  Restore correct layer */
 
863
    /** \todo  Restore correct selection */
 
864
}
 
865
 
 
866
/**  \brief  This function checks the stderr file, and if it has data,
 
867
             shows it in a warning dialog to the user
 
868
     \param  filename  Filename of the stderr file
 
869
*/
 
870
void
 
871
Script::checkStderr (const Glib::ustring &data,
 
872
                           Gtk::MessageType type,
 
873
                     const Glib::ustring &message)
 
874
{
 
875
    Gtk::MessageDialog warning(message, false, type, Gtk::BUTTONS_OK, true);
 
876
    warning.set_resizable(true);
 
877
    GtkWidget *dlg = GTK_WIDGET(warning.gobj());
 
878
    sp_transientize(dlg);
 
879
 
 
880
    Gtk::VBox * vbox = warning.get_vbox();
 
881
 
 
882
    /* Gtk::TextView * textview = new Gtk::TextView(Gtk::TextBuffer::create()); */
 
883
    Gtk::TextView * textview = new Gtk::TextView();
 
884
    textview->set_editable(false);
 
885
    textview->set_wrap_mode(Gtk::WRAP_WORD);
 
886
    textview->show();
 
887
 
 
888
    textview->get_buffer()->set_text(data.c_str());
 
889
 
 
890
    Gtk::ScrolledWindow * scrollwindow = new Gtk::ScrolledWindow();
 
891
    scrollwindow->add(*textview);
 
892
    scrollwindow->set_policy(Gtk::POLICY_AUTOMATIC, Gtk::POLICY_AUTOMATIC);
 
893
    scrollwindow->set_shadow_type(Gtk::SHADOW_IN);
 
894
    scrollwindow->show();
 
895
 
 
896
    vbox->pack_start(*scrollwindow, true, true, 5 /* fix these */);
 
897
 
 
898
    warning.run();
 
899
 
 
900
    return;
 
901
}
 
902
 
 
903
bool
 
904
Script::cancelProcessing (void) {
 
905
    _canceled = true;
 
906
    _main_loop->quit();
 
907
    Glib::spawn_close_pid(_pid);
 
908
 
 
909
    return true;
 
910
}
 
911
 
 
912
 
 
913
/** \brief    This is the core of the extension file as it actually does
 
914
              the execution of the extension.
 
915
    \param    in_command  The command to be executed
 
916
    \param    filein      Filename coming in
 
917
    \param    fileout     Filename of the out file
 
918
    \return   Number of bytes that were read into the output file.
 
919
 
 
920
    The first thing that this function does is build the command to be
 
921
    executed.  This consists of the first string (in_command) and then
 
922
    the filename for input (filein).  This file is put on the command
 
923
    line.
 
924
 
 
925
    The next thing is that this function does is open a pipe to the
 
926
    command and get the file handle in the ppipe variable.  It then
 
927
    opens the output file with the output file handle.  Both of these
 
928
    operations are checked extensively for errors.
 
929
 
 
930
    After both are opened, then the data is copied from the output
 
931
    of the pipe into the file out using fread and fwrite.  These two
 
932
    functions are used because of their primitive nature they make
 
933
    no assumptions about the data.  A buffer is used in the transfer,
 
934
    but the output of fread is stored so the exact number of bytes
 
935
    is handled gracefully.
 
936
 
 
937
    At the very end (after the data has been copied) both of the files
 
938
    are closed, and we return to what we were doing.
 
939
*/
 
940
int
 
941
Script::execute (const std::list<std::string> &in_command,
 
942
                 const std::list<std::string> &in_params,
 
943
                 const Glib::ustring &filein,
 
944
                 file_listener &fileout)
 
945
{
 
946
    g_return_val_if_fail(in_command.size() > 0, 0);
 
947
    // printf("Executing\n");
 
948
 
 
949
    std::vector <std::string> argv;
 
950
 
 
951
/*
 
952
    for (std::list<std::string>::const_iterator i = in_command.begin();
 
953
            i != in_command.end(); i++) {
 
954
        argv.push_back(*i);
 
955
    }
 
956
*/
 
957
    // according to http://www.gtk.org/api/2.6/glib/glib-Spawning-Processes.html spawn quotes parameter containing spaces
 
958
    // we tokenize so that spwan does not need to quote over all params
 
959
    for (std::list<std::string>::const_iterator i = in_command.begin();
 
960
            i != in_command.end(); i++) {
 
961
        std::string param_str = *i;
 
962
        do {
 
963
            //g_message("param: %s", param_str.c_str());
 
964
            size_t first_space = param_str.find_first_of(' ');
 
965
            size_t first_quote = param_str.find_first_of('"');
 
966
            //std::cout << "first space " << first_space << std::endl;
 
967
            //std::cout << "first quote " << first_quote << std::endl;
 
968
 
 
969
            if((first_quote != std::string::npos) && (first_quote == 0)) {
 
970
                size_t next_quote = param_str.find_first_of('"', first_quote + 1);
 
971
                //std::cout << "next quote " << next_quote << std::endl;
 
972
 
 
973
                if(next_quote != std::string::npos) {
 
974
                    //std::cout << "now split " << next_quote << std::endl;
 
975
                    //std::cout << "now split " << param_str.substr(1, next_quote - 1) << std::endl;
 
976
                    //std::cout << "now split " << param_str.substr(next_quote + 1) << std::endl;
 
977
                    std::string part_str = param_str.substr(1, next_quote - 1);
 
978
                    if(part_str.size() > 0)
 
979
                        argv.push_back(part_str);
 
980
                    param_str = param_str.substr(next_quote + 1);
 
981
 
 
982
                }
 
983
                else {
 
984
                    if(param_str.size() > 0)
 
985
                        argv.push_back(param_str);
 
986
                    param_str = "";
 
987
                }
 
988
 
 
989
            }
 
990
            else if(first_space != std::string::npos) {
 
991
                //std::cout << "now split " << first_space << std::endl;
 
992
                //std::cout << "now split " << param_str.substr(0, first_space) << std::endl;
 
993
                //std::cout << "now split " << param_str.substr(first_space + 1) << std::endl;
 
994
                std::string part_str = param_str.substr(0, first_space);
 
995
                if(part_str.size() > 0)
 
996
                    argv.push_back(part_str);
 
997
                param_str = param_str.substr(first_space + 1);
 
998
            }
 
999
            else {
 
1000
                if(param_str.size() > 0)
 
1001
                    argv.push_back(param_str);
 
1002
                param_str = "";
 
1003
            }
 
1004
        } while(param_str.size() > 0);
 
1005
    }
 
1006
 
 
1007
    for (std::list<std::string>::const_iterator i = in_params.begin();
 
1008
            i != in_params.end(); i++) {
 
1009
        //g_message("Script parameter: %s",(*i)g.c_str());
 
1010
        argv.push_back(*i);        
 
1011
    }
 
1012
 
 
1013
    if (!(filein.empty())) {
 
1014
                argv.push_back(filein);
 
1015
    }
 
1016
 
 
1017
    int stdout_pipe, stderr_pipe;
 
1018
 
 
1019
    try {
 
1020
        Inkscape::IO::spawn_async_with_pipes(Glib::get_current_dir(), // working directory
 
1021
                                     argv,  // arg v
 
1022
                                     Glib::SPAWN_SEARCH_PATH /*| Glib::SPAWN_DO_NOT_REAP_CHILD*/,
 
1023
                                     sigc::slot<void>(),
 
1024
                                     &_pid,          // Pid
 
1025
                                     NULL,           // STDIN
 
1026
                                     &stdout_pipe,   // STDOUT
 
1027
                                     &stderr_pipe);  // STDERR
 
1028
    } catch (Glib::SpawnError e) {
 
1029
        printf("Can't Spawn!!! spawn returns: %d\n", e.code());
 
1030
        return 0;
 
1031
    }
 
1032
 
 
1033
    _main_loop = Glib::MainLoop::create(false);
 
1034
 
 
1035
    file_listener fileerr;
 
1036
    fileout.init(stdout_pipe, _main_loop);
 
1037
    fileerr.init(stderr_pipe, _main_loop);
 
1038
 
 
1039
    _canceled = false;
 
1040
    _main_loop->run();
 
1041
 
 
1042
    // Ensure all the data is out of the pipe
 
1043
    while (!fileout.isDead())
 
1044
        fileout.read(Glib::IO_IN);
 
1045
    while (!fileerr.isDead())
 
1046
        fileerr.read(Glib::IO_IN);
 
1047
 
 
1048
    if (_canceled) {
 
1049
        // std::cout << "Script Canceled" << std::endl;
 
1050
        return 0;
 
1051
    }
 
1052
 
 
1053
    Glib::ustring stderr_data = fileerr.string();
 
1054
    if (stderr_data.length() != 0 &&
 
1055
        Inkscape::NSApplication::Application::getUseGui()
 
1056
       ) {
 
1057
        checkStderr(stderr_data, Gtk::MESSAGE_INFO,
 
1058
                                 _("Inkscape has received additional data from the script executed.  "
 
1059
                                   "The script did not return an error, but this may indicate the results will not be as expected."));
 
1060
    }
 
1061
 
 
1062
    Glib::ustring stdout_data = fileout.string();
 
1063
    if (stdout_data.length() == 0) {
 
1064
        return 0;
 
1065
    }
 
1066
 
 
1067
    // std::cout << "Finishing Execution." << std::endl;
 
1068
    return stdout_data.length();
 
1069
}
 
1070
 
 
1071
 
 
1072
 
 
1073
 
 
1074
}  // namespace Implementation
 
1075
}  // namespace Extension
 
1076
}  // namespace Inkscape
 
1077
 
 
1078
/*
 
1079
  Local Variables:
 
1080
  mode:c++
 
1081
  c-file-style:"stroustrup"
 
1082
  c-file-offsets:((innamespace . 0)(inline-open . 0)(case-label . +))
 
1083
  indent-tabs-mode:nil
 
1084
  fill-column:99
 
1085
  End:
 
1086
*/
 
1087
// vim: filetype=cpp:expandtab:shiftwidth=4:tabstop=8:softtabstop=4:encoding=utf-8:textwidth=99 :