]> git.lyx.org Git - lyx.git/blob - src/support/os_cygwin.cpp
2f6b9b4b5740ae79a88746afaf6b37fb7d5a47ca
[lyx.git] / src / support / os_cygwin.cpp
1 /**
2  * \file os_cygwin.cpp
3  * This file is part of LyX, the document processor.
4  * Licence details can be found in the file COPYING.
5  *
6  * \author Ruurd A. Reitsma
7  * \author Claus Hentschel
8  * \author Angus Leeming
9  * \author Enrico Forestieri
10  *
11  * Full author contact details are available in file CREDITS.
12  *
13  * Various OS specific functions
14  */
15
16 #include <config.h>
17
18 #include "support/os.h"
19
20 #include "support/FileName.h"
21 #include "support/lstrings.h"
22 #include "support/debug.h"
23
24 #include <windows.h>
25 #include <io.h>
26 #include <windef.h>
27 #include <shellapi.h>
28 #include <shlwapi.h>
29 #include <limits.h>
30 #include <stdlib.h>
31
32 #include <cygwin/version.h>
33 #include <sys/cygwin.h>
34
35 #include <ostream>
36
37 using namespace std;
38
39 namespace lyx {
40
41 void emergencyCleanup();
42
43 namespace support {
44 namespace os {
45
46 namespace {
47
48 bool windows_style_tex_paths_ = false;
49
50 // In both is_posix_path() and is_windows_path() it is assumed that
51 // a valid posix or pseudo-windows path is passed. They simply tell
52 // whether the path looks posix/pseudo-windows or not.
53
54 bool is_posix_path(string const & p)
55 {
56         return  p.empty() ||
57                 (!contains(p, '\\') && (p.length() <= 1 || p[1] != ':'));
58 }
59
60 // This is a test for a win32 style path with forward slashes (pseudo-windows).
61
62 bool is_windows_path(string const & p)
63 {
64         return p.empty() || (!contains(p, '\\') && p[0] != '/');
65 }
66
67
68 // Starting from Cygwin 1.7, new APIs for path conversions were introduced.
69 // The old ones are now deprecated, so avoid them if we detect a modern Cygwin.
70
71 #if CYGWIN_VERSION_DLL_MAJOR >= 1007
72
73 enum PathStyle {
74         posix = CCP_WIN_A_TO_POSIX | CCP_RELATIVE,
75         windows = CCP_POSIX_TO_WIN_A | CCP_RELATIVE
76 };
77
78
79 /// Convert a path to or from posix style.
80 /// \p p is encoded in local 8bit encoding or utf8.
81 /// The result is returned in the same encoding as \p p.
82 string convert_path(string const & p, PathStyle const & target)
83 {
84         if ((target == posix && is_posix_path(p)) ||
85             (target == windows && is_windows_path(p)))
86                 return p;
87
88         char path_buf[PATH_MAX];
89
90         // cygwin_conv_path does not care about the encoding.
91         if (cygwin_conv_path(target, p.c_str(), path_buf, sizeof(path_buf))) {
92                 lyxerr << "LyX: Cannot convert path: " << p << endl;
93                 return subst(p, '\\', '/');
94         }
95         return subst(path_buf, '\\', '/');
96 }
97
98
99 /// Convert a path list to or from posix style.
100 /// \p p is encoded in local 8bit encoding or utf8.
101 /// The result is returned in the same encoding as \p p.
102 string convert_path_list(string const & p, PathStyle const & target)
103 {
104         if (p.empty())
105                 return p;
106
107         char const * const pc = p.c_str();
108         PathStyle const actual = cygwin_posix_path_list_p(pc) ? posix : windows;
109
110         if (target != actual) {
111                 int const size = cygwin_conv_path_list(target, pc, NULL, 0);
112                 char * ptr = new char[size];
113                 if (ptr && cygwin_conv_path_list(target, pc, ptr, size) == 0) {
114                         string const path_list = subst(ptr, '\\', '/');
115                         delete ptr;
116                         return path_list;
117                 } else
118                         lyxerr << "LyX: Cannot convert path list: " << p << endl;
119         }
120         return subst(p, '\\', '/');
121 }
122
123 #else
124
125 enum PathStyle {
126         posix,
127         windows
128 };
129
130
131 /// Convert a path to or from posix style.
132 /// \p p is encoded in local 8bit encoding or utf8.
133 /// The result is returned in the same encoding as \p p.
134 string convert_path(string const & p, PathStyle const & target)
135 {
136         char path_buf[PATH_MAX];
137
138         if ((target == posix && is_posix_path(p)) ||
139             (target == windows && is_windows_path(p)))
140                 return p;
141
142         path_buf[0] = '\0';
143
144         // cygwin_conv_to_posix_path and cygwin_conv_to_win32_path do not
145         // care about the encoding.
146         if (target == posix)
147                 cygwin_conv_to_posix_path(p.c_str(), path_buf);
148         else
149                 cygwin_conv_to_win32_path(p.c_str(), path_buf);
150
151         return subst(path_buf[0] ? path_buf : p, '\\', '/');
152 }
153
154
155 /// Convert a path list to or from posix style.
156 /// \p p is encoded in local 8bit encoding or utf8.
157 /// The result is returned in the same encoding as \p p.
158 string convert_path_list(string const & p, PathStyle const & target)
159 {
160         if (p.empty())
161                 return p;
162
163         char const * const pc = p.c_str();
164         PathStyle const actual = cygwin_posix_path_list_p(pc) ? posix : windows;
165
166         if (target != actual) {
167                 int const target_size = (target == posix) ?
168                                 cygwin_win32_to_posix_path_list_buf_size(pc) :
169                                 cygwin_posix_to_win32_path_list_buf_size(pc);
170
171                 char * ptr = new char[target_size];
172
173                 if (ptr) {
174                         // FIXME: See comment in convert_path() above
175                         if (target == posix)
176                                 cygwin_win32_to_posix_path_list(pc, ptr);
177                         else
178                                 cygwin_posix_to_win32_path_list(pc, ptr);
179
180                         string path_list = subst(ptr, '\\', '/');
181                         delete ptr;
182                         return path_list;
183                 }
184         }
185
186         return subst(p, '\\', '/');
187 }
188
189 #endif
190
191
192 BOOL terminate_handler(DWORD event)
193 {
194         if (event == CTRL_CLOSE_EVENT
195             || event == CTRL_LOGOFF_EVENT
196             || event == CTRL_SHUTDOWN_EVENT) {
197                 lyx::emergencyCleanup();
198                 return TRUE;
199         }
200         return FALSE;
201 }
202
203 } // namespace anon
204
205 void init(int, char *[])
206 {
207         // Make sure that the TEMP variable is set
208         // and sync the Windows environment.
209         setenv("TEMP", "/tmp", false);
210         cygwin_internal(CW_SYNC_WINENV);
211
212         // Catch shutdown events.
213         SetConsoleCtrlHandler((PHANDLER_ROUTINE)terminate_handler, TRUE);
214 }
215
216
217 string current_root()
218 {
219         return string("/");
220 }
221
222
223 bool isFilesystemCaseSensitive()
224 {
225         return false;
226 }
227
228
229 docstring::size_type common_path(docstring const & p1, docstring const & p2)
230 {
231         docstring::size_type i = 0;
232         docstring::size_type const p1_len = p1.length();
233         docstring::size_type const p2_len = p2.length();
234         while (i < p1_len && i < p2_len && uppercase(p1[i]) == uppercase(p2[i]))
235                 ++i;
236         if ((i < p1_len && i < p2_len)
237             || (i < p1_len && p1[i] != '/' && i == p2_len)
238             || (i < p2_len && p2[i] != '/' && i == p1_len))
239         {
240                 if (i)
241                         --i;     // here was the last match
242                 while (i && p1[i] != '/')
243                         --i;
244         }
245         return i;
246 }
247
248
249 bool path_prefix_is(string const & path, string const & pre)
250 {
251         return path_prefix_is(const_cast<string &>(path), pre, CASE_UNCHANGED);
252 }
253
254
255 bool path_prefix_is(string & path, string const & pre, path_case how)
256 {
257         docstring const p1 = from_utf8(path);
258         docstring const p2 = from_utf8(pre);
259         docstring::size_type const p1_len = p1.length();
260         docstring::size_type const p2_len = p2.length();
261         docstring::size_type common_len = common_path(p1, p2);
262
263         if (p2[p2_len - 1] == '/' && p1_len != p2_len)
264                 ++common_len;
265
266         if (common_len != p2_len)
267                 return false;
268
269         if (how == CASE_ADJUSTED && !prefixIs(path, pre)) {
270                 if (p1_len < common_len)
271                         path = to_utf8(p2.substr(0, p1_len));
272                 else
273                         path = to_utf8(p2 + p1.substr(common_len,
274                                                         p1_len - common_len));
275         }
276
277         return true;
278 }
279
280
281 string external_path(string const & p)
282 {
283         return convert_path(p, PathStyle(posix));
284 }
285
286
287 string internal_path(string const & p)
288 {
289         return convert_path(p, PathStyle(posix));
290 }
291
292
293 string external_path_list(string const & p)
294 {
295         return convert_path_list(p, PathStyle(posix));
296 }
297
298
299 string internal_path_list(string const & p)
300 {
301         return convert_path_list(p, PathStyle(posix));
302 }
303
304
305 string latex_path(string const & p)
306 {
307         // We may need a posix style path or a windows style path (depending
308         // on windows_style_tex_paths_), but we use always forward slashes,
309         // since it gets written into a .tex file.
310
311         if (windows_style_tex_paths_ && FileName::isAbsolute(p)) {
312                 string dos_path = convert_path(p, PathStyle(windows));
313                 LYXERR(Debug::LATEX, "<Path correction for LaTeX> ["
314                         << p << "]->>[" << dos_path << ']');
315                 return dos_path;
316         }
317
318         return convert_path(p, PathStyle(posix));
319 }
320
321
322 bool is_valid_strftime(string const & p)
323 {
324         string::size_type pos = p.find_first_of('%');
325         while (pos != string::npos) {
326                 if (pos + 1 == string::npos)
327                         break;
328                 if (!containsOnly(p.substr(pos + 1, 1),
329                         "aAbBcCdDeEFgGhHIjklmMnOpPrRsStTuUVwWxXyYzZ%+"))
330                         return false;
331                 if (pos + 2 == string::npos)
332                       break;
333                 pos = p.find_first_of('%', pos + 2);
334         }
335         return true;
336 }
337
338
339 // returns a string suitable to be passed to popen when
340 // reading a pipe
341 char const * popen_read_mode()
342 {
343         return "r";
344 }
345
346
347 string const & nulldev()
348 {
349         static string const nulldev_ = "/dev/null";
350         return nulldev_;
351 }
352
353
354 bool is_terminal(io_channel channel)
355 {
356         return isatty(channel);
357 }
358
359
360 shell_type shell()
361 {
362         return UNIX;
363 }
364
365
366 char path_separator()
367 {
368         return ':';
369 }
370
371
372 void windows_style_tex_paths(bool use_windows_paths)
373 {
374         windows_style_tex_paths_ = use_windows_paths;
375 }
376
377
378 bool canAutoOpenFile(string const & ext, auto_open_mode const mode)
379 {
380         if (ext.empty())
381                 return false;
382
383         string const full_ext = "." + ext;
384
385         DWORD bufSize = MAX_PATH + 100;
386         TCHAR buf[MAX_PATH + 100];
387         // reference: http://msdn.microsoft.com/en-us/library/bb773471.aspx
388         char const * action = (mode == VIEW) ? "open" : "edit";
389         return S_OK == AssocQueryString(ASSOCF_INIT_IGNOREUNKNOWN,
390                 ASSOCSTR_EXECUTABLE, full_ext.c_str(), action, buf, &bufSize);
391 }
392
393
394 bool autoOpenFile(string const & filename, auto_open_mode const mode)
395 {
396         // reference: http://msdn.microsoft.com/en-us/library/bb762153.aspx
397         string const win_path = to_local8bit(from_utf8(convert_path(filename, PathStyle(windows))));
398         char const * action = (mode == VIEW) ? "open" : "edit";
399         return reinterpret_cast<int>(ShellExecute(NULL, action,
400                 win_path.c_str(), NULL, NULL, 1)) > 32;
401 }
402
403
404 string real_path(string const & path)
405 {
406         char rpath[PATH_MAX + 1];
407         char * result = realpath(path.c_str(), rpath);
408         return FileName::fromFilesystemEncoding(result ? rpath : path).absFilename();
409 }
410
411 } // namespace os
412 } // namespace support
413 } // namespace lyx