source: git/src/cavernlog.cc @ fb5887c

RELEASE/1.2debug-cidebug-ci-sanitisersstereowalls-data
Last change on this file since fb5887c was fb5887c, checked in by Olly Betts <olly@…>, 14 years ago

src/: Add "Rerun" and "OK" buttons to the CavernLog? window (as
appropriate). (Addition of "OK" button fixes ticket#13). Fix up
handling of splitter window to fix poor handling of various cases.

git-svn-id: file:///home/survex-svn/survex/trunk@3489 4b37db11-9a0c-4f06-9ece-9ab7cdaee568

  • Property mode set to 100644
File size: 8.7 KB
Line 
1/* cavernlog.cc
2 * Run cavern inside an Aven window
3 *
4 * Copyright (C) 2005,2006,2010 Olly Betts
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2 of the License, or
9 * (at your option) any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, write to the Free Software
18 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA  02110-1301 USA
19 */
20
21#ifdef HAVE_CONFIG_H
22# include <config.h>
23#endif
24
25#include "aven.h"
26#include "cavernlog.h"
27#include "filename.h"
28#include "mainfrm.h"
29#include "message.h"
30
31#include <errno.h>
32#include <stdio.h>
33
34// For select():
35#ifdef HAVE_SYS_SELECT_H
36#include <sys/select.h>
37#endif
38#include <sys/time.h>
39#include <sys/types.h>
40#include <unistd.h>
41
42enum { LOG_RERUN = 1234 };
43
44BEGIN_EVENT_TABLE(CavernLogWindow, wxHtmlWindow)
45    EVT_BUTTON(LOG_RERUN, CavernLogWindow::OnRerun)
46    EVT_BUTTON(wxID_OK, CavernLogWindow::OnOK)
47END_EVENT_TABLE()
48
49static wxString escape_for_shell(wxString s, bool protect_dash = false)
50{
51    size_t p = 0;
52#ifdef __WXMSW__
53    bool needs_quotes = false;
54    while (p < s.size()) {
55        if (s[p] == wxT('"')) {
56            s.insert(p, wxT('\\'));
57            ++p;
58            needs_quotes = true;
59        }
60        ++p;
61    }
62    if (needs_quotes) {
63        s.insert(0, wxT('"'));
64        s += wxT('"');
65    }
66#else
67    if (protect_dash && !s.empty() && s[0u] == '-') {
68        // If the filename starts with a '-', protect it from being
69        // treated as an option by prepending "./".
70        s.insert(0, wxT("./"));
71        p = 2;
72    }
73    while (p < s.size()) {
74        // Exclude a few safe characters which are common in filenames
75        if (!isalnum(s[p]) && strchr("/._-", s[p]) == NULL) {
76            s.insert(p, wxT('\\'));
77            ++p;
78        }
79        ++p;
80    }
81#endif
82    return s;
83}
84
85CavernLogWindow::CavernLogWindow(MainFrm * mainfrm_, wxWindow * parent)
86    : wxHtmlWindow(parent), mainfrm(mainfrm_)
87{
88    int fsize = parent->GetFont().GetPointSize();
89    int sizes[7] = { fsize, fsize, fsize, fsize, fsize, fsize, fsize };
90    SetFonts(wxString(), wxString(), sizes);
91}
92
93void
94CavernLogWindow::OnLinkClicked(const wxHtmlLinkInfo &link)
95{
96    wxString href = link.GetHref();
97    wxString title = link.GetTarget();
98    size_t colon = href.rfind(wxT(':'));
99    if (colon != wxString::npos) {
100#ifdef __WXMSW__
101        wxString cmd = wxT("notepad $f");
102#else
103        wxString cmd = wxT("x-terminal-emulator -title $t -e vim -c $l $f");
104        // wxString cmd = "x-terminal-emulator -title $t -e emacs +$l $f";
105        // wxString cmd = "x-terminal-emulator -title $t -e nano +$l $f";
106        // wxString cmd = "x-terminal-emulator -title $t -e jed -g $l $f";
107#endif
108        const char * p = getenv("SURVEXEDITOR");
109        if (p) {
110            cmd = wxString(p, wxConvUTF8);
111            if (!cmd.find(wxT("$f"))) {
112                cmd += wxT(" $f");
113            }
114        }
115        size_t i = 0;
116        while ((i = cmd.find(wxT('$'), i)) != wxString::npos) {
117            if (++i >= cmd.size()) break;
118                switch (cmd[i]) {
119                    case wxT('$'):
120                        cmd.erase(i, 1);
121                        break;
122                    case wxT('f'): {
123                        wxString f = escape_for_shell(href.substr(0, colon), true);
124                        cmd.replace(i - 1, 2, f);
125                        i += f.size() - 1;
126                        break;
127                    }
128                    case wxT('t'): {
129                        wxString t = escape_for_shell(title);
130                        cmd.replace(i - 1, 2, t);
131                        i += t.size() - 1;
132                        break;
133                    }
134                    case wxT('l'): {
135                        wxString l = escape_for_shell(href.substr(colon + 1));
136                        cmd.replace(i - 1, 2, l);
137                        i += l.size() - 1;
138                        break;
139                    }
140                    default:
141                        ++i;
142                }
143        }
144        // FIXME: This should really use fn_str() - currently we probably can't
145        // process a Unicode path on wxmsw.
146        system(cmd.char_str());
147    }
148}
149
150int
151CavernLogWindow::process(const wxString &file)
152{
153    SetFocus();
154    filename = file;
155
156    char *cavern = use_path(msg_exepth(), "cavern");
157#ifdef __WXMSW__
158    SetEnvironmentVariable(wxT("SURVEX_UTF8"), wxT("1"));
159#else
160    setenv("SURVEX_UTF8", "1", 1);
161#endif
162
163    wxString escaped_file = escape_for_shell(file, true);
164    wxString cmd = escape_for_shell(wxString(cavern, wxConvUTF8), false);
165    osfree(cavern);
166    cmd += wxT(" -o ");
167    cmd += escaped_file;
168    cmd += wxT(' ');
169    cmd += escaped_file;
170
171    // FIXME: This should really use fn_str() - currently we probably can't
172    // process a Unicode path on wxmsw.
173    FILE * cavern_out = popen(cmd.char_str(), "r");
174    if (!cavern_out) {
175        wxString m;
176        m.Printf(wmsg(/*Couldn't open pipe: `%s'*/17), cmd.c_str());
177        m += wxT(" (");
178        m += wxString(strerror(errno), wxConvUTF8);
179        m += wxT(')');
180        wxGetApp().ReportError(m);
181        return -2;
182    }
183
184    int cavern_fd;
185#ifdef __WXMSW__
186    cavern_fd = _fileno(cavern_out);
187#else
188    cavern_fd = fileno(cavern_out);
189#endif
190    assert(cavern_fd < FD_SETSIZE); // FIXME we shouldn't just assert, but what else to do?
191    wxString cur;
192    int link_count = 0;
193    // We're only guaranteed one character of pushback by ungetc() but we
194    // need two so for portability we implement the second ourselves.
195    int left = EOF;
196    while (!feof(cavern_out)) {
197        fd_set rfds, efds;
198        FD_ZERO(&rfds);
199        FD_SET(cavern_fd, &rfds);
200        FD_ZERO(&efds);
201        FD_SET(cavern_fd, &efds);
202        // Set timeout to 0.1 seconds.
203        struct timeval timeout;
204        timeout.tv_sec = 0;
205        timeout.tv_usec = 100000;
206        if (select(cavern_fd + 1, &rfds, NULL, &efds, &timeout) == 0) {
207            wxYield();
208            continue;
209        }
210        if (!FD_ISSET(cavern_fd, &rfds)) {
211            // Error, which pclose() should report.
212            break;
213        }
214        int ch;
215        if (left == EOF) {
216            ch = getc(cavern_out);
217            if (ch == EOF) break;
218        } else {
219            ch = left;
220            left = EOF;
221        }
222        // Decode UTF-8 first to avoid security issues with <, >, &, etc
223        // encoded using multibyte encodings.
224        if (ch >= 0xc0 && ch < 0xf0) {
225            int ch1 = getc(cavern_out);
226            if ((ch1 & 0xc0) != 0x80) {
227                left = ch1;
228            } else if (ch < 0xe0) {
229                /* 2 byte sequence */
230                ch = ((ch & 0x1f) << 6) | (ch1 & 0x3f);
231            } else {
232                /* 3 byte sequence */
233                int ch2 = getc(cavern_out);
234                if ((ch2 & 0xc0) != 0x80) {
235                    ungetc(ch2, cavern_out);
236                    left = ch1;
237                } else {
238                    ch = ((ch & 0x1f) << 12) | ((ch1 & 0x3f) << 6) | (ch2 & 0x3f);
239                }
240            }
241        }
242
243        switch (ch) {
244            case '\r':
245                // Ignore.
246                break;
247            case '\n': {
248                if (cur.empty()) continue;
249                size_t colon = cur.find(':');
250#ifdef __WXMSW__
251                // If the path is "C:\path\to\file.svx" then don't split at the
252                // : after the drive letter!  FIXME: better to look for ": "?
253                if (colon == 1) colon = cur.find(':', 2);
254#endif
255                if (colon != wxString::npos) {
256                    size_t colon2 = cur.find(':', colon + 1);
257                    if (colon2 != wxString::npos && colon2 != cur.size() - 1) {
258                        wxString href = cur.substr(0, colon2);
259                        size_t colon3 = colon2;
260                        while (++colon3 < cur.size()) {
261                            if (cur[colon3] != ' ') break;
262                        }
263                        wxString tag = wxT("<a href=\"");
264                        tag.append(cur, 0, colon2);
265                        tag += wxT("\" target=\"");
266                        tag.append(cur, colon3);
267                        tag += wxT("\">");
268                        cur.insert(0, tag);
269                        cur.insert(colon3 + tag.size(), wxT("</a>"));
270                        ++link_count;
271                    }
272                }
273
274                // Save the scrollbar positions.
275                int scroll_x = 0, scroll_y = 0;
276                GetViewStart(&scroll_x, &scroll_y);
277
278                cur += wxT("<br>\n");
279                AppendToPage(cur);
280
281                if (!link_count) {
282                    // Auto-scroll the window until we've reported a warning or
283                    // error.
284                    int x, y;
285                    GetVirtualSize(&x, &y);
286                    int xs, ys;
287                    GetClientSize(&xs, &ys);
288                    y -= ys;
289                    int xu, yu;
290                    GetScrollPixelsPerUnit(&xu, &yu);
291                    Scroll(scroll_x, y / yu);
292                } else {
293                    // Restore the scrollbar positions.
294                    Scroll(scroll_x, scroll_y);
295                }
296
297                cur.clear();
298                Update();
299                break;
300            }
301            case '<':
302                cur += wxT("&lt;");
303                break;
304            case '>':
305                cur += wxT("&gt;");
306                break;
307            case '&':
308                cur += wxT("&amp;");
309                break;
310            case '"':
311                cur += wxT("&#22;");
312                continue;
313            default:
314                if (ch >= 128) {
315                    cur += wxString::Format(wxT("&#%u;"), ch);
316                } else {
317                    cur += (char)ch;
318                }
319        }
320    }
321
322    int retval = pclose(cavern_out);
323    if (retval) {
324        AppendToPage(wxT("<avenbutton default id=1234 name=\"Rerun\">"));
325        if (retval == -1) {
326            wxString m = wxT("Problem running cavern: ");
327            m += wxString(strerror(errno), wxConvUTF8);
328            wxGetApp().ReportError(m);
329            return -2;
330        }
331        return -1;
332    }
333    if (link_count) {
334        AppendToPage(wxT("<avenbutton id=1234 name=\"Rerun\">"));
335        AppendToPage(wxString::Format(wxT("<avenbutton default id=%d>"), (int)wxID_OK));
336        Update();
337    }
338    return link_count;
339}
340
341void
342CavernLogWindow::OnRerun(wxCommandEvent & e)
343{
344    SetPage(wxString());
345    if (process(filename) == 0) {
346        OnOK(e);
347    }
348}
349
350void
351CavernLogWindow::OnOK(wxCommandEvent &)
352{
353    mainfrm->InitialiseAfterLoad(filename);
354}
Note: See TracBrowser for help on using the repository browser.