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 }