1 /* readlink -- display value of a symbolic link.
2    Copyright (C) 2002-2023 Free Software Foundation, Inc.
3 
4    This program is free software: you can redistribute it and/or modify
5    it under the terms of the GNU General Public License as published by
6    the Free Software Foundation, either version 3 of the License, or
7    (at your option) any later version.
8 
9    This program is distributed in the hope that it will be useful,
10    but WITHOUT ANY WARRANTY; without even the implied warranty of
11    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12    GNU General Public License for more details.
13 
14    You should have received a copy of the GNU General Public License
15    along with this program.  If not, see <https://www.gnu.org/licenses/>.  */
16 
17 /* Written by Dmitry V. Levin */
18 
19 #include <config.h>
20 #include <stdio.h>
21 #include <getopt.h>
22 #include <sys/types.h>
23 
24 #include "system.h"
25 #include "canonicalize.h"
26 #include "areadlink.h"
27 
28 /* The official name of this program (e.g., no 'g' prefix).  */
29 #define PROGRAM_NAME "readlink"
30 
31 #define AUTHORS proper_name ("Dmitry V. Levin")
32 
33 /* If true, do not output the trailing newline.  */
34 static bool no_newline;
35 
36 /* If true, report error messages.  */
37 static bool verbose;
38 
39 static struct option const longopts[] =
40 {
41   {"canonicalize", no_argument, nullptr, 'f'},
42   {"canonicalize-existing", no_argument, nullptr, 'e'},
43   {"canonicalize-missing", no_argument, nullptr, 'm'},
44   {"no-newline", no_argument, nullptr, 'n'},
45   {"quiet", no_argument, nullptr, 'q'},
46   {"silent", no_argument, nullptr, 's'},
47   {"verbose", no_argument, nullptr, 'v'},
48   {"zero", no_argument, nullptr, 'z'},
49   {GETOPT_HELP_OPTION_DECL},
50   {GETOPT_VERSION_OPTION_DECL},
51   {nullptr, 0, nullptr, 0}
52 };
53 
54 void
usage(int status)55 usage (int status)
56 {
57   if (status != EXIT_SUCCESS)
58     emit_try_help ();
59   else
60     {
61       printf (_("Usage: %s [OPTION]... FILE...\n"), program_name);
62       fputs (_("Print value of a symbolic link or canonical file name\n\n"),
63              stdout);
64       fputs (_("\
65   -f, --canonicalize            canonicalize by following every symlink in\n\
66                                 every component of the given name recursively;\
67 \n\
68                                 all but the last component must exist\n\
69   -e, --canonicalize-existing   canonicalize by following every symlink in\n\
70                                 every component of the given name recursively,\
71 \n\
72                                 all components must exist\n\
73 "), stdout);
74       fputs (_("\
75   -m, --canonicalize-missing    canonicalize by following every symlink in\n\
76                                 every component of the given name recursively,\
77 \n\
78                                 without requirements on components existence\n\
79   -n, --no-newline              do not output the trailing delimiter\n\
80   -q, --quiet\n\
81   -s, --silent                  suppress most error messages (on by default)\n\
82   -v, --verbose                 report error messages\n\
83   -z, --zero                    end each output line with NUL, not newline\n\
84 "), stdout);
85       fputs (HELP_OPTION_DESCRIPTION, stdout);
86       fputs (VERSION_OPTION_DESCRIPTION, stdout);
87       emit_ancillary_info (PROGRAM_NAME);
88     }
89   exit (status);
90 }
91 
92 int
main(int argc,char ** argv)93 main (int argc, char **argv)
94 {
95   /* If not -1, use this method to canonicalize.  */
96   int can_mode = -1;
97   int status = EXIT_SUCCESS;
98   int optc;
99   bool use_nuls = false;
100 
101   initialize_main (&argc, &argv);
102   set_program_name (argv[0]);
103   setlocale (LC_ALL, "");
104   bindtextdomain (PACKAGE, LOCALEDIR);
105   textdomain (PACKAGE);
106 
107   atexit (close_stdout);
108 
109   while ((optc = getopt_long (argc, argv, "efmnqsvz", longopts, nullptr)) != -1)
110     {
111       switch (optc)
112         {
113         case 'e':
114           can_mode = CAN_EXISTING;
115           break;
116         case 'f':
117           can_mode = CAN_ALL_BUT_LAST;
118           break;
119         case 'm':
120           can_mode = CAN_MISSING;
121           break;
122         case 'n':
123           no_newline = true;
124           break;
125         case 'q':
126         case 's':
127           verbose = false;
128           break;
129         case 'v':
130           verbose = true;
131           break;
132         case 'z':
133           use_nuls = true;
134           break;
135         case_GETOPT_HELP_CHAR;
136         case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
137         default:
138           usage (EXIT_FAILURE);
139         }
140     }
141 
142   if (optind >= argc)
143     {
144       error (0, 0, _("missing operand"));
145       usage (EXIT_FAILURE);
146     }
147 
148   if (argc - optind > 1)
149     {
150       if (no_newline)
151         error (0, 0, _("ignoring --no-newline with multiple arguments"));
152       no_newline = false;
153     }
154 
155   for (; optind < argc; ++optind)
156     {
157       char const *fname = argv[optind];
158       char *value = (can_mode != -1
159                      ? canonicalize_filename_mode (fname, can_mode)
160                      : areadlink_with_size (fname, 63));
161       if (value)
162         {
163           fputs (value, stdout);
164           if (! no_newline)
165             putchar (use_nuls ? '\0' : '\n');
166           free (value);
167         }
168       else
169         {
170           status = EXIT_FAILURE;
171           if (verbose)
172             error (0, errno, "%s", quotef (fname));
173         }
174     }
175 
176   return status;
177 }
178