1// -*- C++ -*-
2/* Copyright (C) 1989, 1990, 1991, 1992, 2000, 2001, 2003, 2005
3   Free Software Foundation, Inc.
4     Written by James Clark (jjc@jclark.com)
5
6This file is part of groff.
7
8groff is free software; you can redistribute it and/or modify it under
9the terms of the GNU General Public License as published by the Free
10Software Foundation; either version 2, or (at your option) any later
11version.
12
13groff is distributed in the hope that it will be useful, but WITHOUT ANY
14WARRANTY; without even the implied warranty of MERCHANTABILITY or
15FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
16for more details.
17
18You should have received a copy of the GNU General Public License along
19with groff; see the file COPYING.  If not, write to the Free Software
20Foundation, 51 Franklin St - Fifth Floor, Boston, MA 02110-1301, USA. */
21
22#include "lib.h"
23
24#include <stdlib.h>
25#include <assert.h>
26#include <errno.h>
27
28#include "searchpath.h"
29#include "nonposix.h"
30
31#ifdef _WIN32
32# include "relocate.h"
33#else
34# define relocate(path) strsave(path)
35#endif
36
37search_path::search_path(const char *envvar, const char *standard,
38			 int add_home, int add_current)
39{
40  char *home = 0;
41  if (add_home)
42    home = getenv("HOME");
43  char *e = 0;
44  if (envvar)
45    e = getenv(envvar);
46  dirs = new char[((e && *e) ? strlen(e) + 1 : 0)
47		  + (add_current ? 1 + 1 : 0)
48		  + ((home && *home) ? strlen(home) + 1 : 0)
49		  + ((standard && *standard) ? strlen(standard) : 0)
50		  + 1];
51  *dirs = '\0';
52  if (e && *e) {
53    strcat(dirs, e);
54    strcat(dirs, PATH_SEP);
55  }
56  if (add_current) {
57    strcat(dirs, ".");
58    strcat(dirs, PATH_SEP);
59  }
60  if (home && *home) {
61    strcat(dirs, home);
62    strcat(dirs, PATH_SEP);
63  }
64  if (standard && *standard)
65    strcat(dirs, standard);
66  init_len = strlen(dirs);
67}
68
69search_path::~search_path()
70{
71  // dirs is always allocated
72  a_delete dirs;
73}
74
75void search_path::command_line_dir(const char *s)
76{
77  char *old = dirs;
78  unsigned old_len = strlen(old);
79  unsigned slen = strlen(s);
80  dirs = new char[old_len + 1 + slen + 1];
81  memcpy(dirs, old, old_len - init_len);
82  char *p = dirs;
83  p += old_len - init_len;
84  if (init_len == 0)
85    *p++ = PATH_SEP_CHAR;
86  memcpy(p, s, slen);
87  p += slen;
88  if (init_len > 0) {
89    *p++ = PATH_SEP_CHAR;
90    memcpy(p, old + old_len - init_len, init_len);
91    p += init_len;
92  }
93  *p++ = '\0';
94  a_delete old;
95}
96
97FILE *search_path::open_file(const char *name, char **pathp)
98{
99  assert(name != 0);
100  if (IS_ABSOLUTE(name) || *dirs == '\0') {
101    FILE *fp = fopen(name, "r");
102    if (fp) {
103      if (pathp)
104	*pathp = strsave(name);
105      return fp;
106    }
107    else
108      return 0;
109  }
110  unsigned namelen = strlen(name);
111  char *p = dirs;
112  for (;;) {
113    char *end = strchr(p, PATH_SEP_CHAR);
114    if (!end)
115      end = strchr(p, '\0');
116    int need_slash = end > p && strchr(DIR_SEPS, end[-1]) == 0;
117    char *origpath = new char[(end - p) + need_slash + namelen + 1];
118    memcpy(origpath, p, end - p);
119    if (need_slash)
120      origpath[end - p] = '/';
121    strcpy(origpath + (end - p) + need_slash, name);
122#if 0
123    fprintf(stderr, "origpath `%s'\n", origpath);
124#endif
125    char *path = relocate(origpath);
126    a_delete origpath;
127#if 0
128    fprintf(stderr, "trying `%s'\n", path);
129#endif
130    FILE *fp = fopen(path, "r");
131    if (fp) {
132      if (pathp)
133	*pathp = path;
134      else
135	a_delete path;
136      return fp;
137    }
138    a_delete path;
139    if (*end == '\0')
140      break;
141    p = end + 1;
142  }
143  return 0;
144}
145
146FILE *search_path::open_file_cautious(const char *name, char **pathp,
147				      const char *mode)
148{
149  if (!mode)
150    mode = "r";
151  bool reading = (strchr(mode, 'r') != 0);
152  if (name == 0 || strcmp(name, "-") == 0) {
153    if (pathp)
154      *pathp = strsave(reading ? "stdin" : "stdout");
155    return (reading ? stdin : stdout);
156  }
157  if (!reading || IS_ABSOLUTE(name) || *dirs == '\0') {
158    FILE *fp = fopen(name, mode);
159    if (fp) {
160      if (pathp)
161	*pathp = strsave(name);
162      return fp;
163    }
164    else
165      return 0;
166  }
167  unsigned namelen = strlen(name);
168  char *p = dirs;
169  for (;;) {
170    char *end = strchr(p, PATH_SEP_CHAR);
171    if (!end)
172      end = strchr(p, '\0');
173    int need_slash = end > p && strchr(DIR_SEPS, end[-1]) == 0;
174    char *origpath = new char[(end - p) + need_slash + namelen + 1];
175    memcpy(origpath, p, end - p);
176    if (need_slash)
177      origpath[end - p] = '/';
178    strcpy(origpath + (end - p) + need_slash, name);
179#if 0
180    fprintf(stderr, "origpath `%s'\n", origpath);
181#endif
182    char *path = relocate(origpath);
183    a_delete origpath;
184#if 0
185    fprintf(stderr, "trying `%s'\n", path);
186#endif
187    FILE *fp = fopen(path, mode);
188    if (fp) {
189      if (pathp)
190	*pathp = path;
191      else
192	a_delete path;
193      return fp;
194    }
195    int err = errno;
196    a_delete path;
197    if (err != ENOENT)
198    {
199      errno = err;
200      return 0;
201    }
202    if (*end == '\0')
203      break;
204    p = end + 1;
205  }
206  errno = ENOENT;
207  return 0;
208}
209