1 /***********************************************************************
2 * *
3 * This software is part of the ast package *
4 * Copyright (c) 1992-2010 AT&T Intellectual Property *
5 * and is licensed under the *
6 * Common Public License, Version 1.0 *
7 * by AT&T Intellectual Property *
8 * *
9 * A copy of the License is available at *
10 * http://www.opensource.org/licenses/cpl1.0.txt *
11 * (with md5 checksum 059e8cd6165cb4c31e351f2b69388fd9) *
12 * *
13 * Information and Software Systems Research *
14 * AT&T Research *
15 * Florham Park NJ *
16 * *
17 * Glenn Fowler <gsf@research.att.com> *
18 * David Korn <dgk@research.att.com> *
19 * *
20 ***********************************************************************/
21 #pragma prototyped
22 /*
23 * David Korn
24 * AT&T Bell Laboratories
25 *
26 * dirname path [suffix]
27 *
28 * print the dirname of a pathname
29 */
30
31 static const char usage[] =
32 "[-?\n@(#)$Id: dirname (AT&T Research) 2009-01-31 $\n]"
33 USAGE_LICENSE
34 "[+NAME?dirname - return directory portion of file name]"
35 "[+DESCRIPTION?\bdirname\b treats \astring\a as a file name and returns "
36 "the name of the directory containing the file name by deleting "
37 "the last component from \astring\a.]"
38 "[+?If \astring\a consists solely of \b/\b characters the output will "
39 "be a single \b/\b unless \bPATH_LEADING_SLASHES\b returned by "
40 "\bgetconf\b(1) is \b1\b and \astring\a consists of multiple "
41 "\b/\b characters in which case \b//\b will be output. "
42 "Otherwise, trailing \b/\b characters are removed, and if "
43 "there are no remaining \b/\b characters in \astring\a, "
44 "the string \b.\b will be written to standard output. "
45 "Otherwise, all characters following the last \b/\b are removed. "
46 "If the remaining string consists solely of \b/\b characters, "
47 "the output will be as if the original string had consisted solely "
48 "as \b/\b characters as described above. Otherwise, all "
49 "trailing slashes are removed and the output will be this string "
50 "unless this string is empty. If empty the output will be \b.\b.]"
51 "[f:file?Print the \b$PATH\b relative regular file path for \astring\a.]"
52 "[r:relative?Print the \b$PATH\b relative readable file path for \astring\a.]"
53 "[x:executable?Print the \b$PATH\b relative executable file path for \astring\a.]"
54 "\n"
55 "\nstring\n"
56 "\n"
57 "[+EXIT STATUS?]{"
58 "[+0?Successful Completion.]"
59 "[+>0?An error occurred.]"
60 "}"
61 "[+SEE ALSO?\bbasename\b(1), \bgetconf\b(1), \bdirname\b(3), \bpathname\b(3)]"
62 ;
63
64 #include <cmd.h>
65
l_dirname(register Sfio_t * outfile,register const char * pathname)66 static void l_dirname(register Sfio_t *outfile, register const char *pathname)
67 {
68 register const char *last;
69 /* go to end of path */
70 for(last=pathname; *last; last++);
71 /* back over trailing '/' */
72 while(last>pathname && *--last=='/');
73 /* back over non-slash chars */
74 for(;last>pathname && *last!='/';last--);
75 if(last==pathname)
76 {
77 /* all '/' or "" */
78 if(*pathname!='/')
79 last = pathname = ".";
80 }
81 else
82 {
83 /* back over trailing '/' */
84 for(;*last=='/' && last > pathname; last--);
85 }
86 /* preserve // */
87 if(last!=pathname && pathname[0]=='/' && pathname[1]=='/')
88 {
89 while(pathname[2]=='/' && pathname<last)
90 pathname++;
91 if(last!=pathname && pathname[0]=='/' && pathname[1]=='/' && *astconf("PATH_LEADING_SLASHES",NiL,NiL)!='1')
92 pathname++;
93 }
94 sfwrite(outfile,pathname,last+1-pathname);
95 sfputc(outfile,'\n');
96 }
97
98 int
b_dirname(int argc,register char * argv[],void * context)99 b_dirname(int argc,register char *argv[], void* context)
100 {
101 register int n;
102 int mode = 0;
103 char buf[PATH_MAX];
104
105 cmdinit(argc, argv, context, ERROR_CATALOG, 0);
106 while (n = optget(argv, usage)) switch (n)
107 {
108 case 'f':
109 mode |= PATH_REGULAR;
110 break;
111 case 'r':
112 mode &= ~PATH_REGULAR;
113 mode |= PATH_READ;
114 break;
115 case 'x':
116 mode |= PATH_EXECUTE;
117 break;
118 case ':':
119 error(2, "%s", opt_info.arg);
120 break;
121 case '?':
122 error(ERROR_usage(2), "%s", opt_info.arg);
123 break;
124 }
125 argv += opt_info.index;
126 argc -= opt_info.index;
127 if(error_info.errors || argc != 1)
128 error(ERROR_usage(2),"%s", optusage(NiL));
129 if(!mode)
130 l_dirname(sfstdout,argv[0]);
131 else if(pathpath(buf, argv[0], "", mode))
132 sfputr(sfstdout, buf, '\n');
133 else
134 error(1|ERROR_WARNING, "%s: relative path not found", argv[0]);
135 return(0);
136 }
137