xref: /aosp_15_r20/external/toybox/toys/other/readlink.c (revision cf5a6c84e2b8763fc1a7db14496fd4742913b199)
1 /* readlink.c - Return string representation of a symbolic link.
2  *
3  * Copyright 2007 Rob Landley <[email protected]>
4 
5 USE_READLINK(NEWTOY(readlink, "<1vnf(canonicalize)emqz[-mef][-qv]", TOYFLAG_USR|TOYFLAG_BIN))
6 USE_REALPATH(NEWTOY(realpath, "<1(relative-base):R(relative-to):s(no-symlinks)LPemqz[-Ps][-LP][-me]", TOYFLAG_USR|TOYFLAG_BIN))
7 
8 config READLINK
9   bool "readlink"
10   default y
11   help
12     usage: readlink [-efmnqz] FILE...
13 
14     With no options, show what symlink points to, return error if not symlink.
15 
16     Options for producing canonical paths (all symlinks/./.. resolved):
17 
18     -e	Canonical path to existing entry (fail if missing)
19     -f	Full path (fail if directory missing)
20     -m	Ignore missing entries, show where it would be
21     -n	No trailing newline
22     -q	Quiet (no error messages)
23     -z	NUL instead of newline
24 
25 config REALPATH
26   bool "realpath"
27   default y
28   help
29     usage: realpath [-LPemqsz] [--relative-base DIR] [-R DIR] FILE...
30 
31     Display the canonical absolute pathname
32 
33     -R Show ../path relative to DIR (--relative-to)
34     -L Logical path (resolve .. before symlinks)
35     -P Physical path (default)
36     -e Canonical path to existing entry (fail if missing)
37     -m Ignore missing entries, show where it would be
38     -q Quiet (no error messages)
39     -s Don't expand symlinks
40     -z NUL instead of newline
41     --relative-base  If path under DIR trim off prefix
42 */
43 
44 #define FOR_realpath
45 #define FORCE_FLAGS
46 #include "toys.h"
47 
48 GLOBALS(
49   char *R, *relative_base;
50 )
51 
52 // test TT.relative_base -RsmLP
53 // Trim .. out early for -s and -L. TODO: in place in the input string.
54 
resolve(char * arg)55 static char *resolve(char *arg)
56 {
57   int flags = FLAG(e) ? ABS_FILE : FLAG(m) ? 0 : ABS_PATH;
58   char *s, *ss = 0, *dd = 0;
59 
60   if (FLAG(s)) flags |= ABS_KEEP;
61   else if (FLAG(L)) arg = dd = xabspath(arg, ABS_KEEP);
62   if (!(s = xabspath(arg, flags)) && !FLAG(q)) perror_msg_raw(arg);
63   free(dd);
64 
65   // Trim off this prefix if path under here
66 
67   if (TT.relative_base) {
68     ss = s;
69     if (strstart(&ss, TT.relative_base) && (!*ss || *ss=='/')) {
70       if (*ss=='/') ss++;
71       ss = xstrdup(!*ss ? "." : ss);
72     } else ss = 0;
73   } else if (TT.R) ss = relative_path(TT.R, s, 0);
74   if (ss) {
75     free(s);
76     s = ss;
77   }
78 
79   return s;
80 }
81 
82 // Resolve command line arguments that can't take part in their own resolution
presolve(char ** s)83 static char *presolve(char **s)
84 {
85   char *ss = *s;
86 
87   if (ss) {
88     *s = 0;
89     if (!(*s = resolve(ss))) xexit();
90   }
91 
92   return ss;
93 }
94 
95 // Uses realpath flag context: flags (1 = resolve, 2 = -n)
do_paths(int flags)96 static void do_paths(int flags)
97 {
98   char **arg, *s;
99 
100   if (!presolve(&TT.relative_base)) presolve(&TT.R);
101 
102   for (arg = toys.optargs; *arg; arg++) {
103     if (!(s = (flags&1) ? resolve(*arg) : xreadlink(*arg))) toys.exitval = 1;
104     else xprintf(((flags&2) && !arg[1]) ? "%s" : "%s%c", s, '\n'*!FLAG(z));
105     free(s);
106   }
107 }
108 
realpath_main(void)109 void realpath_main(void)
110 {
111   do_paths(1);
112 }
113 
114 #define FOR_readlink
115 #include "generated/flags.h"
116 
117 // Convert readlink flag context to realpath (feeding in -nf separately)
readlink_main(void)118 void readlink_main(void)
119 {
120   int nf = (toys.optflags/FLAG_f)|!!(FLAG(m)|FLAG(e));
121 
122   toys.optflags &= FLAG_f-1;
123   if (!FLAG(v)) toys.optflags |= FLAG_q;
124   do_paths(nf);
125 }
126