Mercurial > hg > octave-lyh
comparison liboctave/file-ops.cc @ 10197:4d433bd2d4dc
attempt to avoid trouble with gnulib #defines in a consistent way
author | John W. Eaton <jwe@octave.org> |
---|---|
date | Tue, 26 Jan 2010 00:45:04 -0500 |
parents | 8fa6ce1b21f2 |
children | 025564630c8d |
comparison
equal
deleted
inserted
replaced
10196:69bb6e1e10d2 | 10197:4d433bd2d4dc |
---|---|
31 #include <cstring> | 31 #include <cstring> |
32 | 32 |
33 #include <iostream> | 33 #include <iostream> |
34 #include <vector> | 34 #include <vector> |
35 | 35 |
36 #include <sys/stat.h> | |
36 #include <sys/types.h> | 37 #include <sys/types.h> |
38 #include <unistd.h> | |
37 | 39 |
38 #include <pathmax.h> | 40 #include <pathmax.h> |
39 | 41 |
40 #include "dir-ops.h" | 42 #include "dir-ops.h" |
41 #include "file-ops.h" | 43 #include "file-ops.h" |
45 #include "pathlen.h" | 47 #include "pathlen.h" |
46 #include "quit.h" | 48 #include "quit.h" |
47 #include "str-vec.h" | 49 #include "str-vec.h" |
48 #include "oct-locbuf.h" | 50 #include "oct-locbuf.h" |
49 | 51 |
50 file_ops::static_members *file_ops::static_members::instance = 0; | 52 file_ops *file_ops::instance = 0; |
51 | 53 |
52 file_ops::static_members::static_members (void) | 54 bool |
53 : | 55 file_ops::instance_ok (void) |
56 { | |
57 bool retval = true; | |
58 | |
59 if (! instance) | |
60 { | |
54 #if (defined (OCTAVE_HAVE_WINDOWS_FILESYSTEM) && ! defined (OCTAVE_HAVE_POSIX_FILESYSTEM)) | 61 #if (defined (OCTAVE_HAVE_WINDOWS_FILESYSTEM) && ! defined (OCTAVE_HAVE_POSIX_FILESYSTEM)) |
55 xdir_sep_char ('\\'), | 62 char system_dir_sep_char = '\\'; |
56 xdir_sep_str ("\\"), | 63 std::string system_dir_sep_str = "\\"; |
57 #else | 64 #else |
58 xdir_sep_char ('/'), | 65 char system_dir_sep_char = '/'; |
59 xdir_sep_str ("/"), | 66 std::string system_dir_sep_str = "/"; |
60 #endif | 67 #endif |
61 #if defined (OCTAVE_HAVE_WINDOWS_FILESYSTEM) | 68 #if defined (OCTAVE_HAVE_WINDOWS_FILESYSTEM) |
62 xdir_sep_chars ("/\\") | 69 std::string system_dir_sep_chars = "/\\"; |
63 #else | 70 #else |
64 xdir_sep_chars (xdir_sep_str) | 71 std::string system_dir_sep_chars = system_dir_sep_str; |
65 #endif | 72 #endif |
66 { } | 73 |
67 | 74 instance = new file_ops (system_dir_sep_char, system_dir_sep_str, |
68 bool | 75 system_dir_sep_chars); |
69 file_ops::static_members::instance_ok (void) | 76 |
70 { | 77 if (! instance) |
71 bool retval = true; | 78 { |
72 | 79 (*current_liboctave_error_handler) |
73 if (! instance) | 80 ("unable to create file_ops object!"); |
74 instance = new static_members (); | 81 |
75 | 82 retval = false; |
76 if (! instance) | 83 } |
77 { | |
78 (*current_liboctave_error_handler) | |
79 ("unable to create file_ops::static_members object!"); | |
80 | |
81 retval = false; | |
82 } | 84 } |
83 | 85 |
84 return retval; | 86 return retval; |
85 } | 87 } |
86 | 88 |
87 // We provide a replacement for mkdir(). | 89 // The following tilde-expansion code was stolen and adapted from |
88 | 90 // readline. |
89 int | 91 |
90 file_ops::mkdir (const std::string& nm, mode_t md) | 92 // The default value of tilde_additional_prefixes. This is set to |
91 { | 93 // whitespace preceding a tilde so that simple programs which do not |
92 std::string msg; | 94 // perform any word separation get desired behaviour. |
93 return mkdir (nm, md, msg); | 95 static const char *default_prefixes[] = { " ~", "\t~", ":~", 0 }; |
94 } | 96 |
95 | 97 // The default value of tilde_additional_suffixes. This is set to |
96 int | 98 // whitespace or newline so that simple programs which do not perform |
97 file_ops::mkdir (const std::string& name, mode_t mode, std::string& msg) | 99 // any word separation get desired behaviour. |
100 static const char *default_suffixes[] = { " ", "\n", ":", 0 }; | |
101 | |
102 // If non-null, this contains the address of a function that the | |
103 // application wants called before trying the standard tilde | |
104 // expansions. The function is called with the text sans tilde, and | |
105 // returns a malloc()'ed string which is the expansion, or a NULL | |
106 // pointer if the expansion fails. | |
107 file_ops::tilde_expansion_hook file_ops::tilde_expansion_preexpansion_hook = 0; | |
108 | |
109 // If non-null, this contains the address of a function to call if the | |
110 // standard meaning for expanding a tilde fails. The function is | |
111 // called with the text (sans tilde, as in "foo"), and returns a | |
112 // malloc()'ed string which is the expansion, or a NULL pointer if | |
113 // there is no expansion. | |
114 file_ops::tilde_expansion_hook file_ops::tilde_expansion_failure_hook = 0; | |
115 | |
116 // When non-null, this is a NULL terminated array of strings which are | |
117 // duplicates for a tilde prefix. Bash uses this to expand `=~' and | |
118 // `:~'. | |
119 string_vector file_ops::tilde_additional_prefixes = default_prefixes; | |
120 | |
121 // When non-null, this is a NULL terminated array of strings which | |
122 // match the end of a username, instead of just "/". Bash sets this | |
123 // to `:' and `=~'. | |
124 string_vector file_ops::tilde_additional_suffixes = default_suffixes; | |
125 | |
126 // Find the start of a tilde expansion in S, and return the index | |
127 // of the tilde which starts the expansion. Place the length of the | |
128 // text which identified this tilde starter in LEN, excluding the | |
129 // tilde itself. | |
130 | |
131 static size_t | |
132 tilde_find_prefix (const std::string& s, size_t& len) | |
133 { | |
134 len = 0; | |
135 | |
136 size_t s_len = s.length (); | |
137 | |
138 if (s_len == 0 || s[0] == '~') | |
139 return 0; | |
140 | |
141 string_vector prefixes = file_ops::tilde_additional_prefixes; | |
142 | |
143 if (! prefixes.empty ()) | |
144 { | |
145 for (size_t i = 0; i < s_len; i++) | |
146 { | |
147 for (int j = 0; j < prefixes.length (); j++) | |
148 { | |
149 size_t pfx_len = prefixes[j].length (); | |
150 | |
151 if (prefixes[j].compare (s.substr (i, pfx_len)) == 0) | |
152 { | |
153 len = pfx_len - 1; | |
154 return i + len; | |
155 } | |
156 } | |
157 } | |
158 } | |
159 | |
160 return s_len; | |
161 } | |
162 | |
163 // Find the end of a tilde expansion in S, and return the index | |
164 // of the character which ends the tilde definition. | |
165 | |
166 static size_t | |
167 tilde_find_suffix (const std::string& s) | |
168 { | |
169 size_t s_len = s.length (); | |
170 | |
171 string_vector suffixes = file_ops::tilde_additional_suffixes; | |
172 | |
173 size_t i = 0; | |
174 | |
175 for ( ; i < s_len; i++) | |
176 { | |
177 if (file_ops::is_dir_sep (s[i])) | |
178 break; | |
179 | |
180 if (! suffixes.empty ()) | |
181 { | |
182 for (int j = 0; j < suffixes.length (); j++) | |
183 { | |
184 size_t sfx_len = suffixes[j].length (); | |
185 | |
186 if (suffixes[j].compare (s.substr (i, sfx_len)) == 0) | |
187 return i; | |
188 } | |
189 } | |
190 } | |
191 | |
192 return i; | |
193 } | |
194 | |
195 // Take FNAME and return the tilde prefix we want expanded. | |
196 | |
197 static std::string | |
198 isolate_tilde_prefix (const std::string& fname) | |
199 { | |
200 size_t f_len = fname.length (); | |
201 | |
202 size_t len = 1; | |
203 | |
204 while (len < f_len && ! file_ops::is_dir_sep (fname[len])) | |
205 len++; | |
206 | |
207 return fname.substr (1, len); | |
208 } | |
209 | |
210 // Do the work of tilde expansion on FILENAME. FILENAME starts with a | |
211 // tilde. | |
212 | |
213 static std::string | |
214 tilde_expand_word (const std::string& filename) | |
215 { | |
216 size_t f_len = filename.length (); | |
217 | |
218 if (f_len == 0 || filename[0] != '~') | |
219 return filename; | |
220 | |
221 // A leading `~/' or a bare `~' is *always* translated to the value | |
222 // of $HOME or the home directory of the current user, regardless of | |
223 // any preexpansion hook. | |
224 | |
225 if (f_len == 1 || file_ops::is_dir_sep (filename[1])) | |
226 return octave_env::get_home_directory () + filename.substr (1); | |
227 | |
228 std::string username = isolate_tilde_prefix (filename); | |
229 | |
230 size_t user_len = username.length (); | |
231 | |
232 std::string dirname; | |
233 | |
234 if (file_ops::tilde_expansion_preexpansion_hook) | |
235 { | |
236 std::string expansion | |
237 = file_ops::tilde_expansion_preexpansion_hook (username); | |
238 | |
239 if (! expansion.empty ()) | |
240 return expansion + filename.substr (user_len+1); | |
241 } | |
242 | |
243 // No preexpansion hook, or the preexpansion hook failed. Look in the | |
244 // password database. | |
245 | |
246 octave_passwd pw = octave_passwd::getpwnam (username); | |
247 | |
248 if (! pw) | |
249 { | |
250 // If the calling program has a special syntax for expanding tildes, | |
251 // and we couldn't find a standard expansion, then let them try. | |
252 | |
253 if (file_ops::tilde_expansion_failure_hook) | |
254 { | |
255 std::string expansion | |
256 = file_ops::tilde_expansion_failure_hook (username); | |
257 | |
258 if (! expansion.empty ()) | |
259 dirname = expansion + filename.substr (user_len+1); | |
260 } | |
261 | |
262 // If we don't have a failure hook, or if the failure hook did not | |
263 // expand the tilde, return a copy of what we were passed. | |
264 | |
265 if (dirname.length () == 0) | |
266 dirname = filename; | |
267 } | |
268 else | |
269 dirname = pw.dir () + filename.substr (user_len+1); | |
270 | |
271 return dirname; | |
272 } | |
273 | |
274 // If NAME has a leading ~ or ~user, Unix-style, expand it to the | |
275 // user's home directory. If no ~, or no <pwd.h>, just return NAME. | |
276 | |
277 std::string | |
278 file_ops::tilde_expand (const std::string& name) | |
279 { | |
280 if (name.find ('~') == std::string::npos) | |
281 return name; | |
282 else | |
283 { | |
284 std::string result; | |
285 | |
286 size_t name_len = name.length (); | |
287 | |
288 // Scan through S expanding tildes as we come to them. | |
289 | |
290 size_t pos = 0; | |
291 | |
292 while (1) | |
293 { | |
294 if (pos > name_len) | |
295 break; | |
296 | |
297 size_t len; | |
298 | |
299 // Make START point to the tilde which starts the expansion. | |
300 | |
301 size_t start = tilde_find_prefix (name.substr (pos), len); | |
302 | |
303 result.append (name.substr (pos, start)); | |
304 | |
305 // Advance STRING to the starting tilde. | |
306 | |
307 pos += start; | |
308 | |
309 // Make FINI be the index of one after the last character of the | |
310 // username. | |
311 | |
312 size_t fini = tilde_find_suffix (name.substr (pos)); | |
313 | |
314 // If both START and FINI are zero, we are all done. | |
315 | |
316 if (! (start || fini)) | |
317 break; | |
318 | |
319 // Expand the entire tilde word, and copy it into RESULT. | |
320 | |
321 std::string tilde_word = name.substr (pos, fini); | |
322 | |
323 pos += fini; | |
324 | |
325 std::string expansion = tilde_expand_word (tilde_word); | |
326 | |
327 result.append (expansion); | |
328 } | |
329 | |
330 return result; | |
331 } | |
332 } | |
333 | |
334 // A vector version of the above. | |
335 | |
336 string_vector | |
337 file_ops::tilde_expand (const string_vector& names) | |
338 { | |
339 string_vector retval; | |
340 | |
341 int n = names.length (); | |
342 | |
343 retval.resize (n); | |
344 | |
345 for (int i = 0; i < n; i++) | |
346 retval[i] = tilde_expand (names[i]); | |
347 | |
348 return retval; | |
349 } | |
350 | |
351 std::string | |
352 file_ops::concat (const std::string& dir, const std::string& file) | |
353 { | |
354 return dir.empty () | |
355 ? file | |
356 : (is_dir_sep (dir[dir.length()-1]) | |
357 ? dir + file | |
358 : dir + dir_sep_char () + file); | |
359 } | |
360 | |
361 | |
362 std::string | |
363 file_ops::canonicalize_file_name (const std::string& name) | |
364 { | |
365 std::string msg; | |
366 return canonicalize_file_name (name, msg); | |
367 } | |
368 | |
369 std::string | |
370 file_ops::canonicalize_file_name (const std::string& name, std::string& msg) | |
371 { | |
372 msg = std::string (); | |
373 | |
374 std::string retval; | |
375 | |
376 #if defined (HAVE_CANONICALIZE_FILE_NAME) | |
377 | |
378 char *tmp = ::canonicalize_file_name (name.c_str ()); | |
379 | |
380 if (tmp) | |
381 { | |
382 retval = tmp; | |
383 free (tmp); | |
384 } | |
385 | |
386 #elif defined (HAVE_RESOLVEPATH) | |
387 | |
388 #if !defined (errno) | |
389 extern int errno; | |
390 #endif | |
391 | |
392 #if !defined (__set_errno) | |
393 # define __set_errno(Val) errno = (Val) | |
394 #endif | |
395 | |
396 if (name.empty ()) | |
397 { | |
398 __set_errno (ENOENT); | |
399 return retval; | |
400 } | |
401 | |
402 // All known hosts with resolvepath (e.g. Solaris 7) don't turn | |
403 // relative names into absolute ones, so prepend the working | |
404 // directory if the path is not absolute. | |
405 | |
406 std::string absolute_name | |
407 = octave_env::make_absolute (name, octave_env::getcwd ()); | |
408 | |
409 size_t resolved_size = absolute_name.length (); | |
410 | |
411 while (true) | |
412 { | |
413 resolved_size = 2 * resolved_size + 1; | |
414 | |
415 OCTAVE_LOCAL_BUFFER (char, resolved, resolved_size); | |
416 | |
417 int resolved_len | |
418 = resolvepath (absolute_name.c_str (), resolved, resolved_size); | |
419 | |
420 if (resolved_len < 0) | |
421 break; | |
422 | |
423 if (resolved_len < resolved_size) | |
424 { | |
425 retval = resolved; | |
426 break; | |
427 } | |
428 } | |
429 | |
430 #elif defined (__WIN32__) | |
431 | |
432 int n = 1024; | |
433 | |
434 std::string win_path (n, '\0'); | |
435 | |
436 while (true) | |
437 { | |
438 int status = GetFullPathName (name.c_str (), n, &win_path[0], 0); | |
439 | |
440 if (status == 0) | |
441 break; | |
442 else if (status < n) | |
443 { | |
444 win_path.resize (status); | |
445 retval = win_path; | |
446 break; | |
447 } | |
448 else | |
449 { | |
450 n *= 2; | |
451 win_path.resize (n); | |
452 } | |
453 } | |
454 | |
455 #elif defined (HAVE_REALPATH) | |
456 | |
457 #if !defined (__set_errno) | |
458 # define __set_errno(Val) errno = (Val) | |
459 #endif | |
460 | |
461 if (name.empty ()) | |
462 { | |
463 __set_errno (ENOENT); | |
464 return retval; | |
465 } | |
466 | |
467 OCTAVE_LOCAL_BUFFER (char, buf, PATH_MAX); | |
468 | |
469 if (::realpath (name.c_str (), buf)) | |
470 retval = buf; | |
471 | |
472 #else | |
473 | |
474 // FIXME -- provide replacement here... | |
475 retval = name; | |
476 | |
477 #endif | |
478 | |
479 if (retval.empty ()) | |
480 { | |
481 using namespace std; | |
482 msg = strerror (errno); | |
483 } | |
484 | |
485 return retval; | |
486 } | |
487 | |
488 OCTAVE_API int | |
489 octave_mkdir (const std::string& nm, mode_t md) | |
490 { | |
491 std::string msg; | |
492 return octave_mkdir (nm, md, msg); | |
493 } | |
494 | |
495 OCTAVE_API int | |
496 octave_mkdir (const std::string& name, mode_t mode, std::string& msg) | |
98 { | 497 { |
99 msg = std::string (); | 498 msg = std::string (); |
100 | 499 |
101 int status = -1; | 500 int status = -1; |
102 | 501 |
103 status = octave_mkdir (name.c_str (), mode); | 502 status = mkdir (name.c_str (), mode); |
104 | 503 |
105 if (status < 0) | 504 if (status < 0) |
106 { | 505 { |
107 using namespace std; | 506 using namespace std; |
108 msg = ::strerror (errno); | 507 msg = strerror (errno); |
109 } | 508 } |
110 | 509 |
111 return status; | 510 return status; |
112 } | 511 } |
113 | 512 |
114 // I don't know how to emulate this on systems that don't provide it. | 513 OCTAVE_API int |
115 | 514 octave_mkfifo (const std::string& nm, mode_t md) |
116 int | 515 { |
117 file_ops::mkfifo (const std::string& nm, mode_t md) | 516 std::string msg; |
118 { | 517 return octave_mkfifo (nm, md, msg); |
119 std::string msg; | 518 } |
120 return mkfifo (nm, md, msg); | 519 |
121 } | 520 OCTAVE_API int |
122 | 521 octave_mkfifo (const std::string& name, mode_t mode, std::string& msg) |
123 int | |
124 file_ops::mkfifo (const std::string& name, mode_t mode, std::string& msg) | |
125 { | 522 { |
126 msg = std::string (); | 523 msg = std::string (); |
127 | 524 |
128 int status = -1; | 525 int status = -1; |
129 | 526 |
130 // With gnulib we will always have mkfifo, but some systems like MinGW | 527 // With gnulib we will always have mkfifo, but some systems like MinGW |
131 // don't have working mkfifo functions. On those systems, mkfifo will | 528 // don't have working mkfifo functions. On those systems, mkfifo will |
132 // always return -1 and set errno. | 529 // always return -1 and set errno. |
133 | 530 |
134 status = octave_mkfifo (name.c_str (), mode); | 531 status = mkfifo (name.c_str (), mode); |
135 | 532 |
136 if (status < 0) | 533 if (status < 0) |
137 { | 534 { |
138 using namespace std; | 535 using namespace std; |
139 msg = ::strerror (errno); | 536 msg = strerror (errno); |
140 } | 537 } |
141 | 538 |
142 return status; | 539 return status; |
143 } | 540 } |
144 | 541 |
145 // I don't know how to emulate this on systems that don't provide it. | 542 OCTAVE_API int |
146 | 543 octave_link (const std::string& old_name, const std::string& new_name) |
147 int | 544 { |
148 file_ops::link (const std::string& old_name, const std::string& new_name) | 545 std::string msg; |
149 { | 546 return octave_link (old_name, new_name, msg); |
150 std::string msg; | 547 } |
151 return link (old_name, new_name, msg); | 548 |
152 } | 549 OCTAVE_API int |
153 | 550 octave_link (const std::string& old_name, |
154 int | |
155 file_ops::link (const std::string& old_name, | |
156 const std::string& new_name, std::string& msg) | 551 const std::string& new_name, std::string& msg) |
157 { | 552 { |
158 msg = std::string (); | 553 msg = std::string (); |
159 | 554 |
160 int status = -1; | 555 int status = -1; |
161 | 556 |
162 status = octave_link (old_name.c_str (), new_name.c_str ()); | 557 status = link (old_name.c_str (), new_name.c_str ()); |
163 | 558 |
164 if (status < 0) | 559 if (status < 0) |
165 { | 560 { |
166 using namespace std; | 561 using namespace std; |
167 msg = ::strerror (errno); | 562 msg = strerror (errno); |
168 } | 563 } |
169 | 564 |
170 return status; | 565 return status; |
171 } | 566 } |
172 | 567 |
173 // I don't know how to emulate this on systems that don't provide it. | 568 OCTAVE_API int |
174 | 569 octave_symlink (const std::string& old_name, const std::string& new_name) |
175 int | 570 { |
176 file_ops::symlink (const std::string& old_name, const std::string& new_name) | 571 std::string msg; |
177 { | 572 return octave_symlink (old_name, new_name, msg); |
178 std::string msg; | 573 } |
179 return symlink (old_name, new_name, msg); | 574 |
180 } | 575 OCTAVE_API int |
181 | 576 octave_symlink (const std::string& old_name, |
182 int | |
183 file_ops::symlink (const std::string& old_name, | |
184 const std::string& new_name, std::string& msg) | 577 const std::string& new_name, std::string& msg) |
185 { | 578 { |
186 msg = std::string (); | 579 msg = std::string (); |
187 | 580 |
188 int status = -1; | 581 int status = -1; |
189 | 582 |
190 status = octave_symlink (old_name.c_str (), new_name.c_str ()); | 583 status = symlink (old_name.c_str (), new_name.c_str ()); |
191 | 584 |
192 if (status < 0) | 585 if (status < 0) |
193 { | 586 { |
194 using namespace std; | 587 using namespace std; |
195 msg = ::strerror (errno); | 588 msg = strerror (errno); |
196 } | 589 } |
197 | 590 |
198 return status; | 591 return status; |
199 } | 592 } |
200 | 593 |
201 // We provide a replacement for readlink(). | 594 OCTAVE_API int |
202 | 595 octave_readlink (const std::string& path, std::string& result) |
203 int | 596 { |
204 file_ops::readlink (const std::string& path, std::string& result) | 597 std::string msg; |
205 { | 598 return octave_readlink (path, result, msg); |
206 std::string msg; | 599 } |
207 return readlink (path, result, msg); | 600 |
208 } | 601 OCTAVE_API int |
209 | 602 octave_readlink (const std::string& path, std::string& result, |
210 int | |
211 file_ops::readlink (const std::string& path, std::string& result, | |
212 std::string& msg) | 603 std::string& msg) |
213 { | 604 { |
214 int status = -1; | 605 int status = -1; |
215 | 606 |
216 msg = std::string (); | 607 msg = std::string (); |
217 | 608 |
218 char buf[MAXPATHLEN+1]; | 609 char buf[MAXPATHLEN+1]; |
219 | 610 |
220 status = octave_readlink (path.c_str (), buf, MAXPATHLEN); | 611 status = readlink (path.c_str (), buf, MAXPATHLEN); |
221 | 612 |
222 if (status < 0) | 613 if (status < 0) |
223 { | 614 { |
224 using namespace std; | 615 using namespace std; |
225 msg = ::strerror (errno); | 616 msg = strerror (errno); |
226 } | 617 } |
227 else | 618 else |
228 { | 619 { |
229 buf[status] = '\0'; | 620 buf[status] = '\0'; |
230 result = std::string (buf); | 621 result = std::string (buf); |
232 } | 623 } |
233 | 624 |
234 return status; | 625 return status; |
235 } | 626 } |
236 | 627 |
237 // We provide a replacement for rename(). | 628 OCTAVE_API int |
238 | 629 octave_rename (const std::string& from, const std::string& to) |
239 int | 630 { |
240 file_ops::rename (const std::string& from, const std::string& to) | 631 std::string msg; |
241 { | 632 return octave_rename (from, to, msg); |
242 std::string msg; | 633 } |
243 return rename (from, to, msg); | 634 |
244 } | 635 OCTAVE_API int |
245 | 636 octave_rename (const std::string& from, const std::string& to, |
246 int | |
247 file_ops::rename (const std::string& from, const std::string& to, | |
248 std::string& msg) | 637 std::string& msg) |
249 { | 638 { |
250 int status = -1; | 639 int status = -1; |
251 | 640 |
252 msg = std::string (); | 641 msg = std::string (); |
253 | 642 |
254 status = octave_rename (from.c_str (), to.c_str ()); | 643 status = rename (from.c_str (), to.c_str ()); |
255 | 644 |
256 if (status < 0) | 645 if (status < 0) |
257 { | 646 { |
258 using namespace std; | 647 using namespace std; |
259 msg = ::strerror (errno); | 648 msg = strerror (errno); |
260 } | 649 } |
261 | 650 |
262 return status; | 651 return status; |
263 } | 652 } |
264 | 653 |
265 // We provide a replacement for rmdir(). | 654 OCTAVE_API int |
266 | 655 octave_rmdir (const std::string& name) |
267 int | 656 { |
268 file_ops::rmdir (const std::string& name) | 657 std::string msg; |
269 { | 658 return octave_rmdir (name, msg); |
270 std::string msg; | 659 } |
271 return rmdir (name, msg); | 660 |
272 } | 661 OCTAVE_API int |
273 | 662 octave_rmdir (const std::string& name, std::string& msg) |
274 int | |
275 file_ops::rmdir (const std::string& name, std::string& msg) | |
276 { | 663 { |
277 msg = std::string (); | 664 msg = std::string (); |
278 | 665 |
279 int status = -1; | 666 int status = -1; |
280 | 667 |
281 status = octave_rmdir (name.c_str ()); | 668 status = rmdir (name.c_str ()); |
282 | 669 |
283 if (status < 0) | 670 if (status < 0) |
284 { | 671 { |
285 using namespace std; | 672 using namespace std; |
286 msg = ::strerror (errno); | 673 msg = strerror (errno); |
287 } | 674 } |
288 | 675 |
289 return status; | 676 return status; |
290 } | 677 } |
291 | 678 |
292 // And a version that works recursively. | 679 // And a version that works recursively. |
293 | 680 |
294 int | 681 OCTAVE_API int |
295 file_ops::recursive_rmdir (const std::string& name) | 682 octave_recursive_rmdir (const std::string& name) |
296 { | 683 { |
297 std::string msg; | 684 std::string msg; |
298 return recursive_rmdir (name, msg); | 685 return octave_recursive_rmdir (name, msg); |
299 } | 686 } |
300 | 687 |
301 int | 688 OCTAVE_API int |
302 file_ops::recursive_rmdir (const std::string& name, std::string& msg) | 689 octave_recursive_rmdir (const std::string& name, std::string& msg) |
303 { | 690 { |
304 msg = std::string (); | 691 msg = std::string (); |
305 | 692 |
306 int status = 0; | 693 int status = 0; |
307 | 694 |
328 | 715 |
329 if (fs) | 716 if (fs) |
330 { | 717 { |
331 if (fs.is_dir ()) | 718 if (fs.is_dir ()) |
332 { | 719 { |
333 status = recursive_rmdir (fullnm, msg); | 720 status = octave_recursive_rmdir (fullnm, msg); |
334 | 721 |
335 if (status < 0) | 722 if (status < 0) |
336 break; | 723 break; |
337 } | 724 } |
338 else | 725 else |
339 { | 726 { |
340 status = unlink (fullnm, msg); | 727 status = octave_unlink (fullnm, msg); |
341 | 728 |
342 if (status < 0) | 729 if (status < 0) |
343 break; | 730 break; |
344 } | 731 } |
345 } | 732 } |
351 } | 738 } |
352 | 739 |
353 if (status >= 0) | 740 if (status >= 0) |
354 { | 741 { |
355 dir.close (); | 742 dir.close (); |
356 status = file_ops::rmdir (name, msg); | 743 status = octave_rmdir (name, msg); |
357 } | 744 } |
358 } | 745 } |
359 else | 746 else |
360 { | 747 { |
361 status = -1; | 748 status = -1; |
364 } | 751 } |
365 | 752 |
366 return status; | 753 return status; |
367 } | 754 } |
368 | 755 |
369 std::string | 756 OCTAVE_API int |
370 file_ops::canonicalize_file_name (const std::string& name) | 757 octave_umask (mode_t mode) |
371 { | 758 { |
372 std::string msg; | 759 #if defined (HAVE_UMASK) |
373 return canonicalize_file_name (name, msg); | 760 return umask (mode); |
374 } | 761 #else |
375 | 762 return 0; |
376 std::string | |
377 file_ops::canonicalize_file_name (const std::string& name, std::string& msg) | |
378 { | |
379 msg = std::string (); | |
380 | |
381 std::string retval; | |
382 | |
383 #if defined (HAVE_CANONICALIZE_FILE_NAME) | |
384 | |
385 char *tmp = ::canonicalize_file_name (name.c_str ()); | |
386 | |
387 if (tmp) | |
388 { | |
389 retval = tmp; | |
390 ::free (tmp); | |
391 } | |
392 | |
393 #elif defined (HAVE_RESOLVEPATH) | |
394 | |
395 #if !defined (errno) | |
396 extern int errno; | |
397 #endif | 763 #endif |
398 | 764 } |
399 #if !defined (__set_errno) | 765 |
400 # define __set_errno(Val) errno = (Val) | 766 OCTAVE_API int |
401 #endif | 767 octave_unlink (const std::string& name) |
402 | 768 { |
403 if (name.empty ()) | 769 std::string msg; |
404 { | 770 return octave_unlink (name, msg); |
405 __set_errno (ENOENT); | 771 } |
406 return retval; | 772 |
407 } | 773 OCTAVE_API int |
408 | 774 octave_unlink (const std::string& name, std::string& msg) |
409 // All known hosts with resolvepath (e.g. Solaris 7) don't turn | 775 { |
410 // relative names into absolute ones, so prepend the working | 776 msg = std::string (); |
411 // directory if the path is not absolute. | 777 |
412 | 778 int status = -1; |
413 std::string absolute_name | 779 |
414 = octave_env::make_absolute (name, octave_env::getcwd ()); | 780 status = unlink (name.c_str ()); |
415 | 781 |
416 size_t resolved_size = absolute_name.length (); | 782 if (status < 0) |
417 | 783 { |
418 while (true) | 784 using namespace std; |
419 { | 785 msg = strerror (errno); |
420 resolved_size = 2 * resolved_size + 1; | 786 } |
421 | 787 |
422 OCTAVE_LOCAL_BUFFER (char, resolved, resolved_size); | 788 return status; |
423 | 789 } |
424 int resolved_len | 790 |
425 = ::resolvepath (absolute_name.c_str (), resolved, resolved_size); | 791 OCTAVE_API std::string |
426 | 792 octave_tempnam (const std::string& dir, const std::string& pfx) |
427 if (resolved_len < 0) | 793 { |
428 break; | 794 std::string msg; |
429 | 795 return octave_tempnam (dir, pfx, msg); |
430 if (resolved_len < resolved_size) | 796 } |
431 { | 797 |
432 retval = resolved; | 798 OCTAVE_API std::string |
433 break; | 799 octave_tempnam (const std::string& dir, const std::string& pfx, |
434 } | 800 std::string& msg) |
435 } | |
436 | |
437 #elif defined (__WIN32__) | |
438 | |
439 int n = 1024; | |
440 | |
441 std::string win_path (n, '\0'); | |
442 | |
443 while (true) | |
444 { | |
445 int status = GetFullPathName (name.c_str (), n, &win_path[0], 0); | |
446 | |
447 if (status == 0) | |
448 break; | |
449 else if (status < n) | |
450 { | |
451 win_path.resize (status); | |
452 retval = win_path; | |
453 break; | |
454 } | |
455 else | |
456 { | |
457 n *= 2; | |
458 win_path.resize (n); | |
459 } | |
460 } | |
461 | |
462 #elif defined (HAVE_REALPATH) | |
463 | |
464 #if !defined (__set_errno) | |
465 # define __set_errno(Val) errno = (Val) | |
466 #endif | |
467 | |
468 if (name.empty ()) | |
469 { | |
470 __set_errno (ENOENT); | |
471 return retval; | |
472 } | |
473 | |
474 OCTAVE_LOCAL_BUFFER (char, buf, PATH_MAX); | |
475 | |
476 if (::realpath (name.c_str (), buf)) | |
477 retval = buf; | |
478 | |
479 #else | |
480 | |
481 // FIXME -- provide replacement here... | |
482 retval = name; | |
483 | |
484 #endif | |
485 | |
486 if (retval.empty ()) | |
487 { | |
488 using namespace std; | |
489 msg = ::strerror (errno); | |
490 } | |
491 | |
492 return retval; | |
493 } | |
494 | |
495 // We provide a replacement for tempnam(). | |
496 | |
497 std::string | |
498 file_ops::tempnam (const std::string& dir, const std::string& pfx) | |
499 { | |
500 std::string msg; | |
501 return tempnam (dir, pfx, msg); | |
502 } | |
503 | |
504 std::string | |
505 file_ops::tempnam (const std::string& dir, const std::string& pfx, | |
506 std::string& msg) | |
507 { | 801 { |
508 msg = std::string (); | 802 msg = std::string (); |
509 | 803 |
510 std::string retval; | 804 std::string retval; |
511 | 805 |
512 const char *pdir = dir.empty () ? 0 : dir.c_str (); | 806 const char *pdir = dir.empty () ? 0 : dir.c_str (); |
513 | 807 |
514 const char *ppfx = pfx.empty () ? 0 : pfx.c_str (); | 808 const char *ppfx = pfx.empty () ? 0 : pfx.c_str (); |
515 | 809 |
516 char *tmp = octave_tempnam (pdir, ppfx); | 810 char *tmp = tempnam (pdir, ppfx); |
517 | 811 |
518 if (tmp) | 812 if (tmp) |
519 { | 813 { |
520 retval = tmp; | 814 retval = tmp; |
521 | 815 |
522 ::free (tmp); | 816 free (tmp); |
523 } | 817 } |
524 else | 818 else |
525 { | 819 { |
526 using namespace std; | 820 using namespace std; |
527 msg = ::strerror (errno); | 821 msg = strerror (errno); |
528 } | 822 } |
529 | 823 |
530 return retval; | 824 return retval; |
531 } | 825 } |
532 | |
533 // The following tilde-expansion code was stolen and adapted from | |
534 // readline. | |
535 | |
536 // The default value of tilde_additional_prefixes. This is set to | |
537 // whitespace preceding a tilde so that simple programs which do not | |
538 // perform any word separation get desired behaviour. | |
539 static const char *default_prefixes[] = { " ~", "\t~", ":~", 0 }; | |
540 | |
541 // The default value of tilde_additional_suffixes. This is set to | |
542 // whitespace or newline so that simple programs which do not perform | |
543 // any word separation get desired behaviour. | |
544 static const char *default_suffixes[] = { " ", "\n", ":", 0 }; | |
545 | |
546 // If non-null, this contains the address of a function that the | |
547 // application wants called before trying the standard tilde | |
548 // expansions. The function is called with the text sans tilde, and | |
549 // returns a malloc()'ed string which is the expansion, or a NULL | |
550 // pointer if the expansion fails. | |
551 file_ops::tilde_expansion_hook file_ops::tilde_expansion_preexpansion_hook = 0; | |
552 | |
553 // If non-null, this contains the address of a function to call if the | |
554 // standard meaning for expanding a tilde fails. The function is | |
555 // called with the text (sans tilde, as in "foo"), and returns a | |
556 // malloc()'ed string which is the expansion, or a NULL pointer if | |
557 // there is no expansion. | |
558 file_ops::tilde_expansion_hook file_ops::tilde_expansion_failure_hook = 0; | |
559 | |
560 // When non-null, this is a NULL terminated array of strings which are | |
561 // duplicates for a tilde prefix. Bash uses this to expand `=~' and | |
562 // `:~'. | |
563 string_vector file_ops::tilde_additional_prefixes = default_prefixes; | |
564 | |
565 // When non-null, this is a NULL terminated array of strings which | |
566 // match the end of a username, instead of just "/". Bash sets this | |
567 // to `:' and `=~'. | |
568 string_vector file_ops::tilde_additional_suffixes = default_suffixes; | |
569 | |
570 // Find the start of a tilde expansion in S, and return the index | |
571 // of the tilde which starts the expansion. Place the length of the | |
572 // text which identified this tilde starter in LEN, excluding the | |
573 // tilde itself. | |
574 | |
575 static size_t | |
576 tilde_find_prefix (const std::string& s, size_t& len) | |
577 { | |
578 len = 0; | |
579 | |
580 size_t s_len = s.length (); | |
581 | |
582 if (s_len == 0 || s[0] == '~') | |
583 return 0; | |
584 | |
585 string_vector prefixes = file_ops::tilde_additional_prefixes; | |
586 | |
587 if (! prefixes.empty ()) | |
588 { | |
589 for (size_t i = 0; i < s_len; i++) | |
590 { | |
591 for (int j = 0; j < prefixes.length (); j++) | |
592 { | |
593 size_t pfx_len = prefixes[j].length (); | |
594 | |
595 if (prefixes[j].compare (s.substr (i, pfx_len)) == 0) | |
596 { | |
597 len = pfx_len - 1; | |
598 return i + len; | |
599 } | |
600 } | |
601 } | |
602 } | |
603 | |
604 return s_len; | |
605 } | |
606 | |
607 // Find the end of a tilde expansion in S, and return the index | |
608 // of the character which ends the tilde definition. | |
609 | |
610 static size_t | |
611 tilde_find_suffix (const std::string& s) | |
612 { | |
613 size_t s_len = s.length (); | |
614 | |
615 string_vector suffixes = file_ops::tilde_additional_suffixes; | |
616 | |
617 size_t i = 0; | |
618 | |
619 for ( ; i < s_len; i++) | |
620 { | |
621 if (file_ops::is_dir_sep (s[i])) | |
622 break; | |
623 | |
624 if (! suffixes.empty ()) | |
625 { | |
626 for (int j = 0; j < suffixes.length (); j++) | |
627 { | |
628 size_t sfx_len = suffixes[j].length (); | |
629 | |
630 if (suffixes[j].compare (s.substr (i, sfx_len)) == 0) | |
631 return i; | |
632 } | |
633 } | |
634 } | |
635 | |
636 return i; | |
637 } | |
638 | |
639 // Take FNAME and return the tilde prefix we want expanded. | |
640 | |
641 static std::string | |
642 isolate_tilde_prefix (const std::string& fname) | |
643 { | |
644 size_t f_len = fname.length (); | |
645 | |
646 size_t len = 1; | |
647 | |
648 while (len < f_len && ! file_ops::is_dir_sep (fname[len])) | |
649 len++; | |
650 | |
651 return fname.substr (1, len); | |
652 } | |
653 | |
654 // Do the work of tilde expansion on FILENAME. FILENAME starts with a | |
655 // tilde. | |
656 | |
657 static std::string | |
658 tilde_expand_word (const std::string& filename) | |
659 { | |
660 size_t f_len = filename.length (); | |
661 | |
662 if (f_len == 0 || filename[0] != '~') | |
663 return filename; | |
664 | |
665 // A leading `~/' or a bare `~' is *always* translated to the value | |
666 // of $HOME or the home directory of the current user, regardless of | |
667 // any preexpansion hook. | |
668 | |
669 if (f_len == 1 || file_ops::is_dir_sep (filename[1])) | |
670 return octave_env::get_home_directory () + filename.substr (1); | |
671 | |
672 std::string username = isolate_tilde_prefix (filename); | |
673 | |
674 size_t user_len = username.length (); | |
675 | |
676 std::string dirname; | |
677 | |
678 if (file_ops::tilde_expansion_preexpansion_hook) | |
679 { | |
680 std::string expansion | |
681 = file_ops::tilde_expansion_preexpansion_hook (username); | |
682 | |
683 if (! expansion.empty ()) | |
684 return expansion + filename.substr (user_len+1); | |
685 } | |
686 | |
687 // No preexpansion hook, or the preexpansion hook failed. Look in the | |
688 // password database. | |
689 | |
690 octave_passwd pw = octave_passwd::getpwnam (username); | |
691 | |
692 if (! pw) | |
693 { | |
694 // If the calling program has a special syntax for expanding tildes, | |
695 // and we couldn't find a standard expansion, then let them try. | |
696 | |
697 if (file_ops::tilde_expansion_failure_hook) | |
698 { | |
699 std::string expansion | |
700 = file_ops::tilde_expansion_failure_hook (username); | |
701 | |
702 if (! expansion.empty ()) | |
703 dirname = expansion + filename.substr (user_len+1); | |
704 } | |
705 | |
706 // If we don't have a failure hook, or if the failure hook did not | |
707 // expand the tilde, return a copy of what we were passed. | |
708 | |
709 if (dirname.length () == 0) | |
710 dirname = filename; | |
711 } | |
712 else | |
713 dirname = pw.dir () + filename.substr (user_len+1); | |
714 | |
715 return dirname; | |
716 } | |
717 | |
718 // If NAME has a leading ~ or ~user, Unix-style, expand it to the | |
719 // user's home directory. If no ~, or no <pwd.h>, just return NAME. | |
720 | |
721 std::string | |
722 file_ops::tilde_expand (const std::string& name) | |
723 { | |
724 if (name.find ('~') == std::string::npos) | |
725 return name; | |
726 else | |
727 { | |
728 std::string result; | |
729 | |
730 size_t name_len = name.length (); | |
731 | |
732 // Scan through S expanding tildes as we come to them. | |
733 | |
734 size_t pos = 0; | |
735 | |
736 while (1) | |
737 { | |
738 if (pos > name_len) | |
739 break; | |
740 | |
741 size_t len; | |
742 | |
743 // Make START point to the tilde which starts the expansion. | |
744 | |
745 size_t start = tilde_find_prefix (name.substr (pos), len); | |
746 | |
747 result.append (name.substr (pos, start)); | |
748 | |
749 // Advance STRING to the starting tilde. | |
750 | |
751 pos += start; | |
752 | |
753 // Make FINI be the index of one after the last character of the | |
754 // username. | |
755 | |
756 size_t fini = tilde_find_suffix (name.substr (pos)); | |
757 | |
758 // If both START and FINI are zero, we are all done. | |
759 | |
760 if (! (start || fini)) | |
761 break; | |
762 | |
763 // Expand the entire tilde word, and copy it into RESULT. | |
764 | |
765 std::string tilde_word = name.substr (pos, fini); | |
766 | |
767 pos += fini; | |
768 | |
769 std::string expansion = tilde_expand_word (tilde_word); | |
770 | |
771 result.append (expansion); | |
772 } | |
773 | |
774 return result; | |
775 } | |
776 } | |
777 | |
778 // A vector version of the above. | |
779 | |
780 string_vector | |
781 file_ops::tilde_expand (const string_vector& names) | |
782 { | |
783 string_vector retval; | |
784 | |
785 int n = names.length (); | |
786 | |
787 retval.resize (n); | |
788 | |
789 for (int i = 0; i < n; i++) | |
790 retval[i] = file_ops::tilde_expand (names[i]); | |
791 | |
792 return retval; | |
793 } | |
794 | |
795 int | |
796 file_ops::umask (mode_t mode) | |
797 { | |
798 return octave_umask (mode); | |
799 } | |
800 | |
801 int | |
802 file_ops::unlink (const std::string& name) | |
803 { | |
804 std::string msg; | |
805 return unlink (name, msg); | |
806 } | |
807 | |
808 int | |
809 file_ops::unlink (const std::string& name, std::string& msg) | |
810 { | |
811 msg = std::string (); | |
812 | |
813 int status = -1; | |
814 | |
815 status = octave_unlink (name.c_str ()); | |
816 | |
817 if (status < 0) | |
818 { | |
819 using namespace std; | |
820 msg = ::strerror (errno); | |
821 } | |
822 | |
823 return status; | |
824 } | |
825 | |
826 std::string | |
827 file_ops::concat (const std::string& dir, const std::string& file) | |
828 { | |
829 return dir.empty () | |
830 ? file | |
831 : (is_dir_sep (dir[dir.length()-1]) | |
832 ? dir + file | |
833 : dir + file_ops::dir_sep_char () + file); | |
834 } |