~ubuntu-branches/ubuntu/trusty/bash/trusty-security

« back to all changes in this revision

Viewing changes to builtins/cd.def

  • Committer: Package Import Robot
  • Author(s): Matthias Klose
  • Date: 2014-03-03 22:52:05 UTC
  • mfrom: (1.3.5) (2.2.6 experimental)
  • Revision ID: package-import@ubuntu.com-20140303225205-87ltrt5kspeq0g1b
Tags: 4.3-1ubuntu1
* Merge with Debian; remaining changes:
  - skel.bashrc:
    - Run lesspipe.
    - Enable ls aliases.
    - Set options in ll alias to -alF.
    - Define an alert alias.
    - Enabled colored grep aliases.
  - etc.bash.bashrc:
    - Add sudo hint.

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
This file is cd.def, from which is created cd.c.  It implements the
 
2
builtins "cd" and "pwd" in Bash.
 
3
 
 
4
Copyright (C) 1987-2013 Free Software Foundation, Inc.
 
5
 
 
6
This file is part of GNU Bash, the Bourne Again SHell.
 
7
 
 
8
Bash is free software: you can redistribute it and/or modify
 
9
it under the terms of the GNU General Public License as published by
 
10
the Free Software Foundation, either version 3 of the License, or
 
11
(at your option) any later version.
 
12
 
 
13
Bash is distributed in the hope that it will be useful,
 
14
but WITHOUT ANY WARRANTY; without even the implied warranty of
 
15
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 
16
GNU General Public License for more details.
 
17
 
 
18
You should have received a copy of the GNU General Public License
 
19
along with Bash.  If not, see <http://www.gnu.org/licenses/>.
 
20
 
 
21
$PRODUCES cd.c
 
22
#include <config.h>
 
23
 
 
24
#if defined (HAVE_UNISTD_H)
 
25
#  ifdef _MINIX
 
26
#    include <sys/types.h>
 
27
#  endif
 
28
#  include <unistd.h>
 
29
#endif
 
30
 
 
31
#include "../bashtypes.h"
 
32
#include "posixdir.h"
 
33
#include "posixstat.h"
 
34
#if defined (HAVE_SYS_PARAM_H)
 
35
#include <sys/param.h>
 
36
#endif
 
37
#include <fcntl.h>
 
38
 
 
39
#include <stdio.h>
 
40
 
 
41
#include "../bashansi.h"
 
42
#include "../bashintl.h"
 
43
 
 
44
#include <errno.h>
 
45
#include <tilde/tilde.h>
 
46
 
 
47
#include "../shell.h"
 
48
#include "../flags.h"
 
49
#include "maxpath.h"
 
50
#include "common.h"
 
51
#include "bashgetopt.h"
 
52
 
 
53
#if !defined (errno)
 
54
extern int errno;
 
55
#endif /* !errno */
 
56
 
 
57
extern int posixly_correct;
 
58
extern int array_needs_making;
 
59
extern const char * const bash_getcwd_errstr;
 
60
 
 
61
static int bindpwd __P((int));
 
62
static int setpwd __P((char *));
 
63
static char *resetpwd __P((char *));
 
64
static int change_to_directory __P((char *, int, int));
 
65
 
 
66
static int cdxattr __P((char *, char **));
 
67
static void resetxattr __P((void));
 
68
 
 
69
/* Change this to 1 to get cd spelling correction by default. */
 
70
int cdspelling = 0;
 
71
 
 
72
int cdable_vars;
 
73
 
 
74
static int eflag;       /* file scope so bindpwd() can see it */
 
75
static int xattrflag;   /* O_XATTR support for openat */
 
76
static int xattrfd = -1;
 
77
 
 
78
$BUILTIN cd
 
79
$FUNCTION cd_builtin
 
80
$SHORT_DOC cd [-L|[-P [-e]] [-@]] [dir]
 
81
Change the shell working directory.
 
82
 
 
83
Change the current directory to DIR.  The default DIR is the value of the
 
84
HOME shell variable.
 
85
 
 
86
The variable CDPATH defines the search path for the directory containing
 
87
DIR.  Alternative directory names in CDPATH are separated by a colon (:).
 
88
A null directory name is the same as the current directory.  If DIR begins
 
89
with a slash (/), then CDPATH is not used.
 
90
 
 
91
If the directory is not found, and the shell option `cdable_vars' is set,
 
92
the word is assumed to be  a variable name.  If that variable has a value,
 
93
its value is used for DIR.
 
94
 
 
95
Options:
 
96
    -L  force symbolic links to be followed: resolve symbolic links in
 
97
        DIR after processing instances of `..'
 
98
    -P  use the physical directory structure without following symbolic
 
99
        links: resolve symbolic links in DIR before processing instances
 
100
        of `..'
 
101
    -e  if the -P option is supplied, and the current working directory
 
102
        cannot be determined successfully, exit with a non-zero status
 
103
#if defined (O_XATTR)
 
104
    -@  on systems that support it, present a file with extended attributes
 
105
        as a directory containing the file attributes
 
106
#endif
 
107
 
 
108
The default is to follow symbolic links, as if `-L' were specified.
 
109
`..' is processed by removing the immediately previous pathname component
 
110
back to a slash or the beginning of DIR.
 
111
 
 
112
Exit Status:
 
113
Returns 0 if the directory is changed, and if $PWD is set successfully when
 
114
-P is used; non-zero otherwise.
 
115
$END
 
116
 
 
117
/* Just set $PWD, don't change OLDPWD.  Used by `pwd -P' in posix mode. */
 
118
static int
 
119
setpwd (dirname)
 
120
     char *dirname;
 
121
{
 
122
  int old_anm;
 
123
  SHELL_VAR *tvar;
 
124
 
 
125
  old_anm = array_needs_making;
 
126
  tvar = bind_variable ("PWD", dirname ? dirname : "", 0);
 
127
  if (tvar && readonly_p (tvar))
 
128
    return EXECUTION_FAILURE;
 
129
  if (tvar && old_anm == 0 && array_needs_making && exported_p (tvar))
 
130
    {
 
131
      update_export_env_inplace ("PWD=", 4, dirname ? dirname : "");
 
132
      array_needs_making = 0;
 
133
    }
 
134
  return EXECUTION_SUCCESS;
 
135
}
 
136
 
 
137
static int
 
138
bindpwd (no_symlinks)
 
139
     int no_symlinks;
 
140
{
 
141
  char *dirname, *pwdvar;
 
142
  int old_anm, r;
 
143
  SHELL_VAR *tvar;
 
144
 
 
145
  r = sh_chkwrite (EXECUTION_SUCCESS);
 
146
 
 
147
#define tcwd the_current_working_directory
 
148
  dirname = tcwd ? (no_symlinks ? sh_physpath (tcwd, 0) : tcwd)
 
149
                 : get_working_directory ("cd");
 
150
#undef tcwd
 
151
 
 
152
  old_anm = array_needs_making;
 
153
  pwdvar = get_string_value ("PWD");
 
154
 
 
155
  tvar = bind_variable ("OLDPWD", pwdvar, 0);
 
156
  if (tvar && readonly_p (tvar))
 
157
    r = EXECUTION_FAILURE;
 
158
 
 
159
  if (old_anm == 0 && array_needs_making && exported_p (tvar))
 
160
    {
 
161
      update_export_env_inplace ("OLDPWD=", 7, pwdvar);
 
162
      array_needs_making = 0;
 
163
    }
 
164
 
 
165
  if (setpwd (dirname) == EXECUTION_FAILURE)
 
166
    r = EXECUTION_FAILURE;
 
167
  if (dirname == 0 && eflag)
 
168
    r = EXECUTION_FAILURE;
 
169
 
 
170
  if (dirname && dirname != the_current_working_directory)
 
171
    free (dirname);
 
172
 
 
173
  return (r);
 
174
}
 
175
 
 
176
/* Call get_working_directory to reset the value of
 
177
   the_current_working_directory () */
 
178
static char *
 
179
resetpwd (caller)
 
180
     char *caller;
 
181
{
 
182
  char *tdir;
 
183
      
 
184
  FREE (the_current_working_directory);
 
185
  the_current_working_directory = (char *)NULL;
 
186
  tdir = get_working_directory (caller);
 
187
  return (tdir);
 
188
}
 
189
 
 
190
static int
 
191
cdxattr (dir, ndirp)
 
192
     char *dir;         /* don't assume we can always free DIR */
 
193
     char **ndirp;      /* return new constructed directory name */
 
194
{
 
195
#if defined (O_XATTR)
 
196
  int apfd, fd, r, e;
 
197
  char buf[11+40+40];   /* construct new `fake' path for pwd */
 
198
 
 
199
  apfd = openat (AT_FDCWD, dir, O_RDONLY|O_NONBLOCK);
 
200
  if (apfd < 0)
 
201
    return -1;
 
202
  fd = openat (apfd, ".", O_XATTR);
 
203
  e = errno;
 
204
  close (apfd);         /* ignore close error for now */
 
205
  errno = e;
 
206
  if (fd < 0)
 
207
    return -1;
 
208
  r = fchdir (fd);      /* assume fchdir exists everywhere with O_XATTR */
 
209
  if (r < 0)
 
210
    {
 
211
      close (fd);
 
212
      return -1;
 
213
    }
 
214
  /* NFSv4 and ZFS extended attribute directories do not have names which are
 
215
     visible in the standard Unix directory tree structure.  To ensure we have
 
216
     a valid name for $PWD, we synthesize one under /proc, but to keep that
 
217
     path valid, we need to keep the file descriptor open as long as we are in
 
218
     this directory.  This imposes a certain structure on /proc. */
 
219
  if (ndirp)
 
220
    {
 
221
      sprintf (buf, "/proc/%d/fd/%d", getpid(), fd);
 
222
      *ndirp = savestring (buf);
 
223
    }
 
224
 
 
225
  if (xattrfd >= 0)
 
226
    close (xattrfd);
 
227
  xattrfd = fd;  
 
228
 
 
229
  return r;
 
230
#else
 
231
  return -1;
 
232
#endif
 
233
}
 
234
 
 
235
/* Clean up the O_XATTR baggage.  Currently only closes xattrfd */
 
236
static void
 
237
resetxattr ()
 
238
{
 
239
#if defined (O_XATTR)
 
240
  if (xattrfd >= 0)
 
241
    {
 
242
      close (xattrfd);
 
243
      xattrfd = -1;
 
244
    }
 
245
#else
 
246
  xattrfd = -1;         /* not strictly necessary */
 
247
#endif
 
248
}
 
249
 
 
250
#define LCD_DOVARS      0x001
 
251
#define LCD_DOSPELL     0x002
 
252
#define LCD_PRINTPATH   0x004
 
253
#define LCD_FREEDIRNAME 0x008
 
254
 
 
255
/* This builtin is ultimately the way that all user-visible commands should
 
256
   change the current working directory.  It is called by cd_to_string (),
 
257
   so the programming interface is simple, and it handles errors and
 
258
   restrictions properly. */
 
259
int
 
260
cd_builtin (list)
 
261
     WORD_LIST *list;
 
262
{
 
263
  char *dirname, *cdpath, *path, *temp;
 
264
  int path_index, no_symlinks, opt, lflag;
 
265
 
 
266
#if defined (RESTRICTED_SHELL)
 
267
  if (restricted)
 
268
    {
 
269
      sh_restricted ((char *)NULL);
 
270
      return (EXECUTION_FAILURE);
 
271
    }
 
272
#endif /* RESTRICTED_SHELL */
 
273
 
 
274
  eflag = 0;
 
275
  no_symlinks = no_symbolic_links;
 
276
  xattrflag = 0;
 
277
  reset_internal_getopt ();
 
278
#if defined (O_XATTR)
 
279
  while ((opt = internal_getopt (list, "eLP@")) != -1)
 
280
#else
 
281
  while ((opt = internal_getopt (list, "eLP")) != -1)
 
282
#endif
 
283
    {
 
284
      switch (opt)
 
285
        {
 
286
        case 'P':
 
287
          no_symlinks = 1;
 
288
          break;
 
289
        case 'L':
 
290
          no_symlinks = 0;
 
291
          break;
 
292
        case 'e':
 
293
          eflag = 1;
 
294
          break;
 
295
#if defined (O_XATTR)
 
296
        case '@':
 
297
          xattrflag = 1;
 
298
          break;
 
299
#endif
 
300
        default:
 
301
          builtin_usage ();
 
302
          return (EX_USAGE);
 
303
        }
 
304
    }
 
305
  list = loptend;
 
306
 
 
307
  lflag = (cdable_vars ? LCD_DOVARS : 0) |
 
308
          ((interactive && cdspelling) ? LCD_DOSPELL : 0);
 
309
  if (eflag && no_symlinks == 0)
 
310
    eflag = 0;
 
311
 
 
312
  if (list == 0)
 
313
    {
 
314
      /* `cd' without arguments is equivalent to `cd $HOME' */
 
315
      dirname = get_string_value ("HOME");
 
316
 
 
317
      if (dirname == 0)
 
318
        {
 
319
          builtin_error (_("HOME not set"));
 
320
          return (EXECUTION_FAILURE);
 
321
        }
 
322
      lflag = 0;
 
323
    }
 
324
#if defined (CD_COMPLAINS)
 
325
  else if (list->next)
 
326
    {
 
327
      builtin_error (_("too many arguments"));
 
328
      return (EXECUTION_FAILURE);
 
329
    }
 
330
#endif
 
331
  else if (list->word->word[0] == '-' && list->word->word[1] == '\0')
 
332
    {
 
333
      /* This is `cd -', equivalent to `cd $OLDPWD' */
 
334
      dirname = get_string_value ("OLDPWD");
 
335
 
 
336
      if (dirname == 0)
 
337
        {
 
338
          builtin_error (_("OLDPWD not set"));
 
339
          return (EXECUTION_FAILURE);
 
340
        }
 
341
#if 0
 
342
      lflag = interactive ? LCD_PRINTPATH : 0;
 
343
#else
 
344
      lflag = LCD_PRINTPATH;            /* According to SUSv3 */
 
345
#endif
 
346
    }
 
347
  else if (absolute_pathname (list->word->word))
 
348
    dirname = list->word->word;
 
349
  else if (privileged_mode == 0 && (cdpath = get_string_value ("CDPATH")))
 
350
    {
 
351
      dirname = list->word->word;
 
352
 
 
353
      /* Find directory in $CDPATH. */
 
354
      path_index = 0;
 
355
      while (path = extract_colon_unit (cdpath, &path_index))
 
356
        {
 
357
          /* OPT is 1 if the path element is non-empty */
 
358
          opt = path[0] != '\0';
 
359
          temp = sh_makepath (path, dirname, MP_DOTILDE);
 
360
          free (path);
 
361
 
 
362
          if (change_to_directory (temp, no_symlinks, xattrflag))
 
363
            {
 
364
              /* POSIX.2 says that if a nonempty directory from CDPATH
 
365
                 is used to find the directory to change to, the new
 
366
                 directory name is echoed to stdout, whether or not
 
367
                 the shell is interactive. */
 
368
              if (opt && (path = no_symlinks ? temp : the_current_working_directory))
 
369
                printf ("%s\n", path);
 
370
 
 
371
              free (temp);
 
372
#if 0
 
373
              /* Posix.2 says that after using CDPATH, the resultant
 
374
                 value of $PWD will not contain `.' or `..'. */
 
375
              return (bindpwd (posixly_correct || no_symlinks));
 
376
#else
 
377
              return (bindpwd (no_symlinks));
 
378
#endif
 
379
            }
 
380
          else
 
381
            free (temp);
 
382
        }
 
383
 
 
384
#if 0
 
385
      /* changed for bash-4.2 Posix cd description steps 5-6 */
 
386
      /* POSIX.2 says that if `.' does not appear in $CDPATH, we don't
 
387
         try the current directory, so we just punt now with an error
 
388
         message if POSIXLY_CORRECT is non-zero.  The check for cdpath[0]
 
389
         is so we don't mistakenly treat a CDPATH value of "" as not
 
390
         specifying the current directory. */
 
391
      if (posixly_correct && cdpath[0])
 
392
        {
 
393
          builtin_error ("%s: %s", dirname, strerror (ENOENT));
 
394
          return (EXECUTION_FAILURE);
 
395
        }
 
396
#endif
 
397
    }
 
398
  else
 
399
    dirname = list->word->word;
 
400
 
 
401
  /* When we get here, DIRNAME is the directory to change to.  If we
 
402
     chdir successfully, just return. */
 
403
  if (change_to_directory (dirname, no_symlinks, xattrflag))
 
404
    {
 
405
      if (lflag & LCD_PRINTPATH)
 
406
        printf ("%s\n", dirname);
 
407
      return (bindpwd (no_symlinks));
 
408
    }
 
409
 
 
410
  /* If the user requests it, then perhaps this is the name of
 
411
     a shell variable, whose value contains the directory to
 
412
     change to. */
 
413
  if (lflag & LCD_DOVARS)
 
414
    {
 
415
      temp = get_string_value (dirname);
 
416
      if (temp && change_to_directory (temp, no_symlinks, xattrflag))
 
417
        {
 
418
          printf ("%s\n", temp);
 
419
          return (bindpwd (no_symlinks));
 
420
        }
 
421
    }
 
422
 
 
423
  /* If the user requests it, try to find a directory name similar in
 
424
     spelling to the one requested, in case the user made a simple
 
425
     typo.  This is similar to the UNIX 8th and 9th Edition shells. */
 
426
  if (lflag & LCD_DOSPELL)
 
427
    {
 
428
      temp = dirspell (dirname);
 
429
      if (temp && change_to_directory (temp, no_symlinks, xattrflag))
 
430
        {
 
431
          printf ("%s\n", temp);
 
432
          free (temp);
 
433
          return (bindpwd (no_symlinks));
 
434
        }
 
435
      else
 
436
        FREE (temp);
 
437
    }
 
438
 
 
439
  builtin_error ("%s: %s", dirname, strerror (errno));
 
440
  return (EXECUTION_FAILURE);
 
441
}
 
442
 
 
443
$BUILTIN pwd
 
444
$FUNCTION pwd_builtin
 
445
$SHORT_DOC pwd [-LP]
 
446
Print the name of the current working directory.
 
447
 
 
448
Options:
 
449
  -L    print the value of $PWD if it names the current working
 
450
        directory
 
451
  -P    print the physical directory, without any symbolic links
 
452
 
 
453
By default, `pwd' behaves as if `-L' were specified.
 
454
 
 
455
Exit Status:
 
456
Returns 0 unless an invalid option is given or the current directory
 
457
cannot be read.
 
458
$END
 
459
 
 
460
/* Non-zero means that pwd always prints the physical directory, without
 
461
   symbolic links. */
 
462
static int verbatim_pwd;
 
463
 
 
464
/* Print the name of the current working directory. */
 
465
int
 
466
pwd_builtin (list)
 
467
     WORD_LIST *list;
 
468
{
 
469
  char *directory;
 
470
  int opt, pflag;
 
471
 
 
472
  verbatim_pwd = no_symbolic_links;
 
473
  pflag = 0;
 
474
  reset_internal_getopt ();
 
475
  while ((opt = internal_getopt (list, "LP")) != -1)
 
476
    {
 
477
      switch (opt)
 
478
        {
 
479
        case 'P':
 
480
          verbatim_pwd = pflag = 1;
 
481
          break;
 
482
        case 'L':
 
483
          verbatim_pwd = 0;
 
484
          break;
 
485
        default:
 
486
          builtin_usage ();
 
487
          return (EX_USAGE);
 
488
        }
 
489
    }
 
490
  list = loptend;
 
491
 
 
492
#define tcwd the_current_working_directory
 
493
 
 
494
  directory = tcwd ? (verbatim_pwd ? sh_physpath (tcwd, 0) : tcwd)
 
495
                   : get_working_directory ("pwd");
 
496
 
 
497
  /* Try again using getcwd() if canonicalization fails (for instance, if
 
498
     the file system has changed state underneath bash). */
 
499
  if ((tcwd && directory == 0) ||
 
500
      (posixly_correct && same_file (".", tcwd, (struct stat *)0, (struct stat *)0) == 0))
 
501
    {
 
502
      if (directory && directory != tcwd)
 
503
        free (directory);
 
504
      directory = resetpwd ("pwd");
 
505
    }
 
506
 
 
507
#undef tcwd
 
508
 
 
509
  if (directory)
 
510
    {
 
511
      opt = EXECUTION_SUCCESS;
 
512
      printf ("%s\n", directory);
 
513
      /* This is dumb but posix-mandated. */
 
514
      if (posixly_correct && pflag)
 
515
        opt = setpwd (directory);
 
516
      if (directory != the_current_working_directory)
 
517
        free (directory);
 
518
      return (sh_chkwrite (opt));
 
519
    }
 
520
  else
 
521
    return (EXECUTION_FAILURE);
 
522
}
 
523
 
 
524
/* Do the work of changing to the directory NEWDIR.  Handle symbolic
 
525
   link following, etc.  This function *must* return with
 
526
   the_current_working_directory either set to NULL (in which case
 
527
   getcwd() will eventually be called), or set to a string corresponding
 
528
   to the working directory.  Return 1 on success, 0 on failure. */
 
529
 
 
530
static int
 
531
change_to_directory (newdir, nolinks, xattr)
 
532
     char *newdir;
 
533
     int nolinks, xattr;
 
534
{
 
535
  char *t, *tdir, *ndir;
 
536
  int err, canon_failed, r, ndlen, dlen;
 
537
 
 
538
  tdir = (char *)NULL;
 
539
 
 
540
  if (the_current_working_directory == 0)
 
541
    {
 
542
      t = get_working_directory ("chdir");
 
543
      FREE (t);
 
544
    }
 
545
 
 
546
  t = make_absolute (newdir, the_current_working_directory);
 
547
 
 
548
  /* TDIR is either the canonicalized absolute pathname of NEWDIR
 
549
     (nolinks == 0) or the absolute physical pathname of NEWDIR
 
550
     (nolinks != 0). */
 
551
  tdir = nolinks ? sh_physpath (t, 0)
 
552
                 : sh_canonpath (t, PATH_CHECKDOTDOT|PATH_CHECKEXISTS);
 
553
 
 
554
  ndlen = strlen (newdir);
 
555
  dlen = strlen (t);
 
556
 
 
557
  /* Use the canonicalized version of NEWDIR, or, if canonicalization
 
558
     failed, use the non-canonical form. */
 
559
  canon_failed = 0;
 
560
  if (tdir && *tdir)
 
561
    free (t);
 
562
  else
 
563
    {
 
564
      FREE (tdir);
 
565
      tdir = t;
 
566
      canon_failed = 1;
 
567
    }
 
568
 
 
569
  /* In POSIX mode, if we're resolving symlinks logically and sh_canonpath
 
570
     returns NULL (because it checks the path, it will return NULL if the
 
571
     resolved path doesn't exist), fail immediately. */
 
572
  if (posixly_correct && nolinks == 0 && canon_failed && (errno != ENAMETOOLONG || ndlen > PATH_MAX))
 
573
    {
 
574
#if defined ENAMETOOLONG
 
575
      if (errno != ENOENT && errno != ENAMETOOLONG)
 
576
#else
 
577
      if (errno != ENOENT)
 
578
#endif
 
579
        errno = ENOTDIR;
 
580
      free (tdir);
 
581
      return (0);
 
582
    }
 
583
 
 
584
#if defined (O_XATTR)
 
585
  if (xattrflag)
 
586
    {
 
587
      r = cdxattr (nolinks ? newdir : tdir, &ndir);
 
588
      if (r >= 0)
 
589
        {
 
590
          canon_failed = 0;
 
591
          free (tdir);
 
592
          tdir = ndir;
 
593
        }
 
594
      else
 
595
        {
 
596
          err = errno;
 
597
          free (tdir);
 
598
          errno = err;
 
599
          return (0);           /* no xattr */
 
600
        }
 
601
    }
 
602
  else
 
603
#endif
 
604
    {
 
605
      r = chdir (nolinks ? newdir : tdir);
 
606
      if (r >= 0)
 
607
        resetxattr ();
 
608
    }
 
609
 
 
610
  /* If the chdir succeeds, update the_current_working_directory. */
 
611
  if (r == 0)
 
612
    {
 
613
      /* If canonicalization failed, but the chdir succeeded, reset the
 
614
         shell's idea of the_current_working_directory. */
 
615
      if (canon_failed)
 
616
        {
 
617
          t = resetpwd ("cd");
 
618
          if (t == 0)
 
619
            set_working_directory (tdir);
 
620
          else
 
621
            free (t);
 
622
        }
 
623
      else
 
624
        set_working_directory (tdir);
 
625
 
 
626
      free (tdir);
 
627
      return (1);
 
628
    }
 
629
 
 
630
  /* We failed to change to the appropriate directory name.  If we tried
 
631
     what the user passed (nolinks != 0), punt now. */
 
632
  if (nolinks)
 
633
    {
 
634
      free (tdir);
 
635
      return (0);
 
636
    }
 
637
 
 
638
  err = errno;
 
639
 
 
640
  /* We're not in physical mode (nolinks == 0), but we failed to change to
 
641
     the canonicalized directory name (TDIR).  Try what the user passed
 
642
     verbatim. If we succeed, reinitialize the_current_working_directory. */
 
643
  if (chdir (newdir) == 0)
 
644
    {
 
645
      t = resetpwd ("cd");
 
646
      if (t == 0)
 
647
        set_working_directory (tdir);
 
648
      else
 
649
        free (t);
 
650
 
 
651
      r = 1;
 
652
    }
 
653
  else
 
654
    {
 
655
      errno = err;
 
656
      r = 0;
 
657
    }
 
658
 
 
659
  free (tdir);
 
660
  return r;
 
661
}