ead2d6bd5c55f951b2bb7cc774098c6baed09fc4
[platform/upstream/groff.git] / src / libs / libgroff / relocate.cpp
1 // -*- C++ -*-
2 /* Provide relocation for macro and font files.
3    Copyright (C) 2005-2014  Free Software Foundation, Inc.
4
5    This program is free software; you can redistribute it and/or modify it
6    under the terms of the GNU Library General Public License as published
7    by the Free Software Foundation, either version 3 of the License, or
8    (at your option) any later version.
9
10    This program is distributed in the hope that it will be useful,
11    but WITHOUT ANY WARRANTY; without even the implied warranty of
12    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13    Library General Public License for more details.
14
15    You should have received a copy of the GNU Library General Public License
16    along with this program. If not, see <http://www.gnu.org/licenses/>.  */
17
18 // Made after relocation code in kpathsea and gettext.
19
20 #include "lib.h"
21
22 #include <errno.h>
23 #include <stdlib.h>
24
25 #include "defs.h"
26 #include "posix.h"
27 #include "nonposix.h"
28 #include "relocate.h"
29
30 #if defined _WIN32
31 # define WIN32_LEAN_AND_MEAN
32 # include <windows.h>
33 #endif
34
35 #define INSTALLPATHLEN (sizeof(INSTALLPATH) - 1)
36 #ifndef DEBUG
37 # define DEBUG 0
38 #endif
39
40 extern "C" const char *program_name;
41
42 // The prefix (parent directory) corresponding to the binary.
43 char *curr_prefix = 0;
44 size_t curr_prefix_len = 0;
45
46 // Return the directory part of a filename, or `.' if no path separators.
47 char *xdirname(char *s)
48 {
49   static const char dot[] = ".";
50   if (!s)
51     return 0;
52   // DIR_SEPS[] are possible directory separator characters, see nonposix.h.
53   // We want the rightmost separator of all possible ones.
54   // Example: d:/foo\\bar.
55   char *p = strrchr(s, DIR_SEPS[0]);
56   const char *sep = &DIR_SEPS[1];
57   while (*sep) {
58     char *p1 = strrchr(s, *sep);
59     if (p1 && (!p || p1 > p))
60       p = p1;
61     sep++;
62   }
63   if (p)
64     *p = '\0';
65   else
66     s = (char *)dot;
67   return s;
68 }
69
70 // Return the full path of NAME along the path PATHP.
71 // Adapted from search_path::open_file in searchpath.cpp.
72 char *searchpath(const char *name, const char *pathp)
73 {
74   char *path;
75   if (!name || !*name)
76     return 0;
77 #if DEBUG
78   fprintf(stderr, "searchpath: pathp: `%s'\n", pathp);
79   fprintf(stderr, "searchpath: trying `%s'\n", name);
80 #endif
81   // Try first NAME as such; success if NAME is an absolute filename,
82   // or if NAME is found in the current directory.
83   if (!access (name, F_OK)) {
84     path = new char[path_name_max()];
85 #ifdef _WIN32
86     path = _fullpath(path, name, path_name_max());
87 #else
88     path = realpath(name, path);
89 #endif
90 #if DEBUG
91     fprintf(stderr, "searchpath: found `%s'\n", path);
92 #endif
93     return path;
94   }
95   // Secondly, try the current directory.
96   // Now search along PATHP.
97   size_t namelen = strlen(name);
98   char *p = (char *)pathp;
99   for (;;) {
100     char *end = strchr(p, PATH_SEP_CHAR);
101     if (!end)
102       end = strchr(p, '\0');
103     int need_slash = end > p && strchr(DIR_SEPS, end[-1]) == 0;
104     path = new char[end - p + need_slash + namelen + 1];
105     memcpy(path, p, end - p);
106     if (need_slash)
107       path[end - p] = '/';
108     strcpy(path + (end - p) + need_slash, name);
109 #if DEBUG
110     fprintf(stderr, "searchpath: trying `%s'\n", path);
111 #endif
112     if (!access(path, F_OK)) {
113 #if DEBUG
114       fprintf(stderr, "searchpath: found `%s'\n", name);
115 #endif
116       return path;
117     }
118     a_delete path;
119     if (*end == '\0')
120       break;
121     p = end + 1;
122   }
123   return 0;
124 }
125
126 // Search NAME along PATHP with the elements of PATHEXT in turn added.
127 char *searchpathext(const char *name, const char *pathext, const char *pathp)
128 {
129   char *found = 0;
130   char *tmpathext = strsave(pathext);   // strtok modifies this string,
131                                         // so make a copy
132   char *ext = strtok(tmpathext, PATH_SEP);
133   while (ext) {
134     char *namex = new char[strlen(name) + strlen(ext) + 1];
135     strcpy(namex, name);
136     strcat(namex, ext);
137     found = searchpath(namex, pathp);
138     a_delete namex;
139     if (found)
140        break;
141     ext = strtok(0, PATH_SEP);
142   }
143   a_delete tmpathext;
144   return found;
145 }
146
147 // Convert an MS path to a POSIX path.
148 char *msw2posixpath(char *path)
149 {
150   char *s = path;
151   while (*s) {
152     if (*s == '\\')
153       *s = '/';
154     s++;
155   }
156   return path;
157 }
158
159 // Compute the current prefix.
160 void set_current_prefix()
161 {
162   char *pathextstr;
163   curr_prefix = new char[path_name_max()];
164   // Obtain the full path of the current binary;
165   // using GetModuleFileName on MS-Windows,
166   // and searching along PATH on other systems.
167 #ifdef _WIN32
168   int len = GetModuleFileName(0, curr_prefix, path_name_max());
169   if (len)
170     len = GetShortPathName(curr_prefix, curr_prefix, path_name_max());
171 # if DEBUG
172   fprintf(stderr, "curr_prefix: %s\n", curr_prefix);
173 # endif /* DEBUG */
174 #else /* !_WIN32 */
175   curr_prefix = searchpath(program_name, getenv("PATH"));
176   if (!curr_prefix && !strchr(program_name, '.')) {     // try with extensions
177     pathextstr = strsave(getenv("PATHEXT"));
178     if (!pathextstr)
179       pathextstr = strsave(PATH_EXT);
180     curr_prefix = searchpathext(program_name, pathextstr, getenv("PATH"));
181     a_delete pathextstr;
182   }
183   if (!curr_prefix)
184     return;
185 #endif /* !_WIN32 */
186   msw2posixpath(curr_prefix);
187 #if DEBUG
188   fprintf(stderr, "curr_prefix: %s\n", curr_prefix);
189 #endif
190   curr_prefix = xdirname(curr_prefix);  // directory of executable
191   curr_prefix = xdirname(curr_prefix);  // parent directory of executable
192   curr_prefix_len = strlen(curr_prefix);
193 #if DEBUG
194   fprintf(stderr, "curr_prefix: %s\n", curr_prefix);
195   fprintf(stderr, "curr_prefix_len: %d\n", curr_prefix_len);
196 #endif
197 }
198
199 // Strip the installation prefix and replace it
200 // with the current installation prefix; return the relocated path.
201 char *relocatep(const char *path)
202 {
203 #if DEBUG
204   fprintf(stderr, "relocatep: path = %s\n", path);
205   fprintf(stderr, "relocatep: INSTALLPATH = %s\n", INSTALLPATH);
206   fprintf(stderr, "relocatep: INSTALLPATHLEN = %d\n", INSTALLPATHLEN);
207 #endif
208   if (!curr_prefix)
209     set_current_prefix();
210   if (strncmp(INSTALLPATH, path, INSTALLPATHLEN))
211     return strsave(path);
212   char *relative_path = (char *)path + INSTALLPATHLEN;
213   size_t relative_path_len = strlen(relative_path);
214   char *relocated_path = new char[curr_prefix_len + relative_path_len + 1];
215   strcpy(relocated_path, curr_prefix);
216   strcat(relocated_path, relative_path);
217 #if DEBUG
218   fprintf(stderr, "relocated_path: %s\n", relocated_path);
219 #endif /* DEBUG */
220   return relocated_path;
221 }
222
223 // Return the original pathname if it exists;
224 // otherwise return the relocated path.
225 char *relocate(const char *path)
226 {
227   char *p;
228   if (access(path, F_OK))
229     p = relocatep(path);
230   else
231     p = strsave(path);
232 #if DEBUG
233   fprintf (stderr, "relocate: %s\n", p);
234 #endif
235   return p;
236 }