I would like to propose a new command for bash:
ca [path]
It returns the canonical path based on the current working directory and
entered path.
If the current working directory has been traversed through a symbolic
link, then listing a higher level path using dotdot's do not always show
I am looking for. Below is a trivial example:
ro...@otto:~/test$ ls -lR
.:
total 8
drwxr-xr-x 3 rolfb rolfb 4096 2009-02-05 23:59 A
drwxr-xr-x 2 rolfb rolfb 4096 2009-02-06 00:09 B
./A:
total 4
-rw-r--r-- 1 rolfb rolfb 0 2009-02-05 23:59 a
drwxr-xr-x 2 rolfb rolfb 4096 2009-02-05 23:58 AA
./A/AA:
total 0
./B:
total 0
-rw-r--r-- 1 rolfb rolfb 0 2009-02-05 23:59 b
lrwxrwxrwx 1 rolfb rolfb 7 2009-02-06 00:09 BB -> ../A/AA
~/test$ cd B/BB
### No this is not what I was looking for
~/test/B/BB$ ls -l ..
total 4
-rw-r--r-- 1 rolfb rolfb 0 2009-02-05 23:59 a
drwxr-xr-x 2 rolfb rolfb 4096 2009-02-05 23:58 AA
~/test/B/BB$ ls ../b
ls: cannot access ../b: No such file or directory
### This is what I wanted to see
~/test/B/BB$ ca ..
/home/rolfb/test/B
~/test/B/BB$ ca ..|xargs ls -l
total 0
-rw-r--r-- 1 rolfb rolfb 0 2009-02-05 23:59 b
lrwxrwxrwx 1 rolfb rolfb 7 2009-02-06 00:09 BB -> ../A/AA
~/test/B/BB$ ca ../b
/home/rolfb/test/B/b
~/test/B/BB$ ca ../b|xargs ls -l
-rw-r--r-- 1 rolfb rolfb 0 2009-02-05 23:59 /home/rolfb/test/B/b
### System config
~/test/B/BB$ uname -a
Linux otto 2.6.27-11-generic #1 SMP Thu Jan 29 19:24:39 UTC 2009 i686
GNU/Linux
~/test/B/BB$ bash --version
GNU bash, version 3.2.39(1)-release (i486-pc-linux-gnu)
Copyright (C) 2007 Free Software Foundation, Inc.
~/test/B/BB$ cat /etc/lsb-release
DISTRIB_ID=Ubuntu
DISTRIB_RELEASE=8.10
DISTRIB_CODENAME=intrepid
DISTRIB_DESCRIPTION="Ubuntu 8.10"
I have not done any shell, glibc or kernel development in the past and
have two minor items I am a little uncertain about; free versus FREE,
and the _is_cygdrive call. But unless there is an interest in this
command, that will be a mute point.
The files I modified:
/home/rolfb/bash/bash-3.2# find . -type f -name "*orig"
./bash/builtins/builtext.h.orig
./bash/builtins/cd.def.orig
./bash/builtins/builtins.c.orig
./bash/externs.h.orig
./bash/lib/sh/pathcanon.c.orig
/home/rolfb/bash/bash-3.2# diff -u bash/externs.h.orig bash/externs.h
--- bash/externs.h.orig 2006-07-27 20:40:49.000000000 -0500
+++ bash/externs.h 2009-02-11 19:34:25.000000000 -0600
@@ -214,6 +214,7 @@
#define PATH_CHECKEXISTS 0x0002
#define PATH_HARDPATH 0x0004
#define PATH_NOALLOC 0x0008
+#define PATH_NOTDIRONLY 0x0010
extern char *sh_canonpath __P((char *, int));
/home/rolfb/bash/bash-3.2# diff -u bash/builtins/builtext.h.orig
bash/builtins/builtext.h
--- bash/builtins/builtext.h.orig 2009-02-10 13:43:40.000000000 -0600
+++ bash/builtins/builtext.h 2009-02-11 00:30:19.000000000 -0600
@@ -21,6 +21,8 @@
extern int caller_builtin __P((WORD_LIST *));
extern char * const caller_doc[];
#endif /* DEBUGGER */
+extern int ca_builtin __P((WORD_LIST *));
+extern char * const ca_doc[];
extern int cd_builtin __P((WORD_LIST *));
extern char * const cd_doc[];
extern int pwd_builtin __P((WORD_LIST *));
/home/rolfb/bash/bash-3.2# diff -u bash/builtins/builtins.c.orig
bash/builtins/builtins.c
--- bash/builtins/builtins.c.orig 2009-02-10 13:43:40.000000000 -0600
+++ bash/builtins/builtins.c 2009-02-11 13:16:01.000000000 -0600
@@ -60,6 +60,8 @@
{ "caller", caller_builtin, BUILTIN_ENABLED | STATIC_BUILTIN, caller_doc,
"caller [EXPR]", (char *)NULL },
#endif /* DEBUGGER */
+ { "ca", ca_builtin, BUILTIN_ENABLED | STATIC_BUILTIN, ca_doc,
+ "ca [path]", (char *)NULL },
{ "cd", cd_builtin, BUILTIN_ENABLED | STATIC_BUILTIN, cd_doc,
"cd [-L|-P] [dir]", (char *)NULL },
{ "pwd", pwd_builtin, BUILTIN_ENABLED | STATIC_BUILTIN, pwd_doc,
@@ -329,6 +331,15 @@
(char *)NULL
};
#endif /* DEBUGGER */
+char * const ca_doc[] = {
+#if defined (HELP_BUILTIN)
+N_("Prints the canonical path which is derived from the supplied path\n\
+ concatentated to the current working directory. If a path is not\n\
+ supplied then the canonical path is only derived from the\n\
+ current working directory."),
+#endif /* HELP_BUILTIN */
+ (char *)NULL
+};
char * const cd_doc[] = {
#if defined (HELP_BUILTIN)
N_("Change the current directory to DIR. The variable $HOME is the\n\
/home/rolfb/bash/bash-3.2# diff -u bash/builtins/cd.def.orig
bash/builtins/cd.def
--- bash/builtins/cd.def.orig 2006-07-27 20:35:36.000000000 -0500
+++ bash/builtins/cd.def 2009-02-11 21:18:25.000000000 -0600
@@ -70,6 +70,15 @@
int cdable_vars;
+$BUILTIN ca
+$FUNCTION ca_builtin
+$SHORT_DOC ca [path]
+Prints the canonical path which is derived from the supplied path
+concatentated to the current working directory. If a path is not
+supplied then the canonical path is only derived from the
+current working directory.
+$END
+
$BUILTIN cd
$FUNCTION cd_builtin
$SHORT_DOC cd [-L|-P] [dir]
@@ -314,6 +323,72 @@
return (EXECUTION_FAILURE);
}
+/* Prints the canonical path which is derived from the supplied path
+ concatentated to the current working directory. If a path is not
+ supplied then the canonical path is only derived from the
+ current working directory. */
+int
+ca_builtin (list)
+ WORD_LIST *list;
+{
+ char *dirname, *t, *tdir;
+
+#if defined (RESTRICTED_SHELL)
+ if (restricted)
+ {
+ sh_restricted ((char *)NULL);
+ return (EXECUTION_FAILURE);
+ }
+#endif /* RESTRICTED_SHELL */
+
+ reset_internal_getopt ();
+ if (internal_getopt (list, "") != -1)
+ {
+ builtin_usage ();
+ return (EXECUTION_FAILURE);
+ }
+ list = loptend;
+
+ if (the_current_working_directory == 0)
+ {
+ t = get_working_directory ("ca");
+ FREE (t);
+ }
+
+ if (list == 0)
+ {
+ printf("%s\n", the_current_working_directory);
+ }
+ else
+ {
+ dirname = list->word->word;
+
+ tdir = (char *)NULL;
+
+ t = make_absolute (dirname, the_current_working_directory);
+
+ tdir = sh_canonpath (t,
PATH_CHECKDOTDOT|PATH_CHECKEXISTS|PATH_NOTDIRONLY);
+
+ free(t);
+
+ if (tdir == NULL)
+ {
+ if (errno)
+ builtin_error ("%s: %s", dirname, strerror (errno));
+ else
+ builtin_error ("%s: %s", dirname, "Path not found");
+ return (EXECUTION_FAILURE);
+ }
+ else
+ {
+ printf("%s\n", tdir);
+ free (tdir);
+ }
+ }
+
+ return 0;
+}
+
$BUILTIN pwd
$FUNCTION pwd_builtin
$SHORT_DOC pwd [-LP]
/home/rolfb/bash/bash-3.2# diff -u bash/lib/sh/pathcanon.c.orig
bash/lib/sh/pathcanon.c
--- bash/lib/sh/pathcanon.c.orig 2005-02-22 16:40:56.000000000 -0600
+++ bash/lib/sh/pathcanon.c 2009-02-11 20:47:08.000000000 -0600
@@ -88,6 +88,24 @@
return l;
}
+/* Return 1 if PATH is found. */
+static int
+_path_ispath (path)
+ char *path;
+{
+ int l;
+ struct stat sb;
+
+ /* This should leave errno set to the correct value. */
+ errno = 0;
+ l = stat (path, &sb) == 0;
+#if defined (__CYGWIN__)
+ if (l == 0)
+ l = _is_cygdrive (path);
+#endif
+ return l;
+}
+
/* Canonicalize PATH, and return a new path. The new path differs from
PATH
in that:
Multple `/'s are collapsed to a single `/'.
@@ -160,12 +178,17 @@
if (flags & PATH_CHECKDOTDOT)
{
char c;
+ int path_found;
/* Make sure what we have so far corresponds to a valid
path before we chop some of it off. */
c = *q;
*q = '\0';
- if (_path_isdir (result) == 0)
+ if (flags & PATH_NOTDIRONLY)
+ path_found = _path_ispath (result);
+ else
+ path_found = _path_isdir (result);
+ if (!path_found)
{
if ((flags & PATH_NOALLOC) == 0)
free (result);
@@ -198,12 +221,17 @@
if (flags & PATH_CHECKEXISTS)
{
char c;
+ int path_found;
/* Make sure what we have so far corresponds to a valid
path before we chop some of it off. */
c = *q;
*q = '\0';
- if (_path_isdir (result) == 0)
+ if (flags & PATH_NOTDIRONLY)
+ path_found = _path_ispath (result);
+ else
+ path_found = _path_isdir (result);
+ if (!path_found)
{
if ((flags & PATH_NOALLOC) == 0)
free (result);
Rolf Brudeseth