1151497Sru// -*- C++ -*-
2151497Sru/* Provide relocation for macro and font files.
3151497Sru   Copyright (C) 2005 Free Software Foundation, Inc.
4151497Sru
5151497Sru   This program is free software; you can redistribute it and/or modify it
6151497Sru   under the terms of the GNU Library General Public License as published
7151497Sru   by the Free Software Foundation; either version 2, or (at your option)
8151497Sru   any later version.
9151497Sru
10151497Sru   This program is distributed in the hope that it will be useful,
11151497Sru   but WITHOUT ANY WARRANTY; without even the implied warranty of
12151497Sru   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
13151497Sru   Library General Public License for more details.
14151497Sru
15151497Sru   You should have received a copy of the GNU Library General Public
16151497Sru   License along with this program; if not, write to the Free Software
17151497Sru   Foundation, Inc., 51 Franklin St - Fifth Floor, Boston, MA 02110-1301,
18151497Sru   USA.  */
19151497Sru
20151497Sru// Made after relocation code in kpathsea and gettext.
21151497Sru
22151497Sru#include "lib.h"
23151497Sru
24151497Sru#include <errno.h>
25151497Sru#include <stdlib.h>
26151497Sru
27151497Sru#include "defs.h"
28151497Sru#include "posix.h"
29151497Sru#include "nonposix.h"
30151497Sru#include "relocate.h"
31151497Sru
32151497Sru#if defined _WIN32
33151497Sru# define WIN32_LEAN_AND_MEAN
34151497Sru# include <windows.h>
35151497Sru#endif
36151497Sru
37151497Sru#define INSTALLPATHLEN (sizeof(INSTALLPATH) - 1)
38151497Sru#ifndef DEBUG
39151497Sru# define DEBUG 0
40151497Sru#endif
41151497Sru
42151497Sruextern "C" const char *program_name;
43151497Sru
44151497Sru// The prefix (parent directory) corresponding to the binary.
45151497Sruchar *curr_prefix = 0;
46151497Srusize_t curr_prefix_len = 0;
47151497Sru
48151497Sru// Return the directory part of a filename, or `.' if no path separators.
49151497Sruchar *xdirname(char *s)
50151497Sru{
51151497Sru  static const char dot[] = ".";
52151497Sru  if (!s)
53151497Sru    return 0;
54151497Sru  // DIR_SEPS[] are possible directory separator characters, see nonposix.h.
55151497Sru  // We want the rightmost separator of all possible ones.
56151497Sru  // Example: d:/foo\\bar.
57151497Sru  char *p = strrchr(s, DIR_SEPS[0]);
58151497Sru  const char *sep = &DIR_SEPS[1];
59151497Sru  while (*sep) {
60151497Sru    char *p1 = strrchr(s, *sep);
61151497Sru    if (p1 && (!p || p1 > p))
62151497Sru      p = p1;
63151497Sru    sep++;
64151497Sru  }
65151497Sru  if (p)
66151497Sru    *p = '\0';
67151497Sru  else
68151497Sru    s = (char *)dot;
69151497Sru  return s;
70151497Sru}
71151497Sru
72151497Sru// Return the full path of NAME along the path PATHP.
73151497Sru// Adapted from search_path::open_file in searchpath.cpp.
74151497Sruchar *searchpath(const char *name, const char *pathp)
75151497Sru{
76151497Sru  char *path;
77151497Sru  if (!name || !*name)
78151497Sru    return 0;
79151497Sru#if DEBUG
80151497Sru  fprintf(stderr, "searchpath: pathp: `%s'\n", pathp);
81151497Sru  fprintf(stderr, "searchpath: trying `%s'\n", name);
82151497Sru#endif
83151497Sru  // Try first NAME as such; success if NAME is an absolute filename,
84151497Sru  // or if NAME is found in the current directory.
85151497Sru  if (!access (name, F_OK)) {
86151497Sru    path = new char[path_name_max()];
87151497Sru#ifdef _WIN32
88151497Sru    path = _fullpath(path, name, path_name_max());
89151497Sru#else
90151497Sru    path = realpath(name, path);
91151497Sru#endif
92151497Sru#if DEBUG
93151497Sru    fprintf(stderr, "searchpath: found `%s'\n", path);
94151497Sru#endif
95151497Sru    return path;
96151497Sru  }
97151497Sru  // Secondly, try the current directory.
98151497Sru  // Now search along PATHP.
99151497Sru  size_t namelen = strlen(name);
100151497Sru  char *p = (char *)pathp;
101151497Sru  for (;;) {
102151497Sru    char *end = strchr(p, PATH_SEP_CHAR);
103151497Sru    if (!end)
104151497Sru      end = strchr(p, '\0');
105151497Sru    int need_slash = end > p && strchr(DIR_SEPS, end[-1]) == 0;
106151497Sru    path = new char[end - p + need_slash + namelen + 1];
107151497Sru    memcpy(path, p, end - p);
108151497Sru    if (need_slash)
109151497Sru      path[end - p] = '/';
110151497Sru    strcpy(path + (end - p) + need_slash, name);
111151497Sru#if DEBUG
112151497Sru    fprintf(stderr, "searchpath: trying `%s'\n", path);
113151497Sru#endif
114151497Sru    if (!access(path, F_OK)) {
115151497Sru#if DEBUG
116151497Sru      fprintf(stderr, "searchpath: found `%s'\n", name);
117151497Sru#endif
118151497Sru      return path;
119151497Sru    }
120151497Sru    a_delete path;
121151497Sru    if (*end == '\0')
122151497Sru      break;
123151497Sru    p = end + 1;
124151497Sru  }
125151497Sru  return 0;
126151497Sru}
127151497Sru
128151497Sru// Search NAME along PATHP with the elements of PATHEXT in turn added.
129151497Sruchar *searchpathext(const char *name, const char *pathext, const char *pathp)
130151497Sru{
131151497Sru  char *found = 0;
132151497Sru  char *tmpathext = strsave(pathext);	// strtok modifies this string,
133151497Sru					// so make a copy
134151497Sru  char *ext = strtok(tmpathext, PATH_SEP);
135151497Sru  while (ext) {
136151497Sru    char *namex = new char[strlen(name) + strlen(ext) + 1];
137151497Sru    strcpy(namex, name);
138151497Sru    strcat(namex, ext);
139151497Sru    found = searchpath(namex, pathp);
140151497Sru    a_delete namex;
141151497Sru    if (found)
142151497Sru       break;
143151497Sru    ext = strtok(0, PATH_SEP);
144151497Sru  }
145151497Sru  a_delete tmpathext;
146151497Sru  return found;
147151497Sru}
148151497Sru
149151497Sru// Convert an MS path to a POSIX path.
150151497Sruchar *msw2posixpath(char *path)
151151497Sru{
152151497Sru  char *s = path;
153151497Sru  while (*s) {
154151497Sru    if (*s == '\\')
155151497Sru      *s = '/';
156151497Sru    *s++;
157151497Sru  }
158151497Sru  return path;
159151497Sru}
160151497Sru
161151497Sru// Compute the current prefix.
162151497Sruvoid set_current_prefix()
163151497Sru{
164151497Sru  char *pathextstr;
165151497Sru  curr_prefix = new char[path_name_max()];
166151497Sru  // Obtain the full path of the current binary;
167151497Sru  // using GetModuleFileName on MS-Windows,
168151497Sru  // and searching along PATH on other systems.
169151497Sru#ifdef _WIN32
170151497Sru  int len = GetModuleFileName(0, curr_prefix, path_name_max());
171151497Sru  if (len)
172151497Sru    len = GetShortPathName(curr_prefix, curr_prefix, path_name_max());
173151497Sru# if DEBUG
174151497Sru  fprintf(stderr, "curr_prefix: %s\n", curr_prefix);
175151497Sru# endif /* DEBUG */
176151497Sru#else /* !_WIN32 */
177151497Sru  curr_prefix = searchpath(program_name, getenv("PATH"));
178151497Sru  if (!curr_prefix && !strchr(program_name, '.')) {	// try with extensions
179151497Sru    pathextstr = strsave(getenv("PATHEXT"));
180151497Sru    if (!pathextstr)
181151497Sru      pathextstr = strsave(PATH_EXT);
182151497Sru    curr_prefix = searchpathext(program_name, pathextstr, getenv("PATH"));
183151497Sru    a_delete pathextstr;
184151497Sru  }
185151497Sru  if (!curr_prefix)
186151497Sru    return;
187151497Sru#endif /* !_WIN32 */
188151497Sru  msw2posixpath(curr_prefix);
189151497Sru#if DEBUG
190151497Sru  fprintf(stderr, "curr_prefix: %s\n", curr_prefix);
191151497Sru#endif
192151497Sru  curr_prefix = xdirname(curr_prefix);	// directory of executable
193151497Sru  curr_prefix = xdirname(curr_prefix);	// parent directory of executable
194151497Sru  curr_prefix_len = strlen(curr_prefix);
195151497Sru#if DEBUG
196151497Sru  fprintf(stderr, "curr_prefix: %s\n", curr_prefix);
197151497Sru  fprintf(stderr, "curr_prefix_len: %d\n", curr_prefix_len);
198151497Sru#endif
199151497Sru}
200151497Sru
201151497Sru// Strip the installation prefix and replace it
202151497Sru// with the current installation prefix; return the relocated path.
203151497Sruchar *relocatep(const char *path)
204151497Sru{
205151497Sru#if DEBUG
206151497Sru  fprintf(stderr, "relocatep: path = %s\n", path);
207151497Sru  fprintf(stderr, "relocatep: INSTALLPATH = %s\n", INSTALLPATH);
208151497Sru  fprintf(stderr, "relocatep: INSTALLPATHLEN = %d\n", INSTALLPATHLEN);
209151497Sru#endif
210151497Sru  if (!curr_prefix)
211151497Sru    set_current_prefix();
212151497Sru  if (strncmp(INSTALLPATH, path, INSTALLPATHLEN))
213151497Sru    return strsave(path);
214151497Sru  char *relative_path = (char *)path + INSTALLPATHLEN;
215151497Sru  size_t relative_path_len = strlen(relative_path);
216151497Sru  char *relocated_path = new char[curr_prefix_len + relative_path_len + 1];
217151497Sru  strcpy(relocated_path, curr_prefix);
218151497Sru  strcat(relocated_path, relative_path);
219151497Sru#if DEBUG
220151497Sru  fprintf(stderr, "relocated_path: %s\n", relocated_path);
221151497Sru#endif /* DEBUG */
222151497Sru  return relocated_path;
223151497Sru}
224151497Sru
225151497Sru// Return the original pathname if it exists;
226151497Sru// otherwise return the relocated path.
227151497Sruchar *relocate(const char *path)
228151497Sru{
229151497Sru  char *p;
230151497Sru  if (access(path, F_OK))
231151497Sru    p = relocatep(path);
232151497Sru  else
233151497Sru    p = strsave(path);
234151497Sru#if DEBUG
235151497Sru  fprintf (stderr, "relocate: %s\n", p);
236151497Sru#endif
237151497Sru  return p;
238151497Sru}
239