1114402Sru// -*- C++ -*-
2151497Sru/* Copyright (C) 1989, 1990, 1991, 1992, 2000, 2001, 2003, 2005
3114402Sru   Free Software Foundation, Inc.
4114402Sru     Written by James Clark (jjc@jclark.com)
5114402Sru
6114402SruThis file is part of groff.
7114402Sru
8114402Srugroff is free software; you can redistribute it and/or modify it under
9114402Sruthe terms of the GNU General Public License as published by the Free
10114402SruSoftware Foundation; either version 2, or (at your option) any later
11114402Sruversion.
12114402Sru
13114402Srugroff is distributed in the hope that it will be useful, but WITHOUT ANY
14114402SruWARRANTY; without even the implied warranty of MERCHANTABILITY or
15114402SruFITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License
16114402Srufor more details.
17114402Sru
18114402SruYou should have received a copy of the GNU General Public License along
19114402Sruwith groff; see the file COPYING.  If not, write to the Free Software
20151497SruFoundation, 51 Franklin St - Fifth Floor, Boston, MA 02110-1301, USA. */
21114402Sru
22114402Sru#include "lib.h"
23114402Sru
24114402Sru#include <stdlib.h>
25114402Sru#include <assert.h>
26151497Sru#include <errno.h>
27114402Sru
28114402Sru#include "searchpath.h"
29114402Sru#include "nonposix.h"
30114402Sru
31151497Sru#ifdef _WIN32
32151497Sru# include "relocate.h"
33151497Sru#else
34151497Sru# define relocate(path) strsave(path)
35151497Sru#endif
36151497Sru
37114402Srusearch_path::search_path(const char *envvar, const char *standard,
38114402Sru			 int add_home, int add_current)
39114402Sru{
40114402Sru  char *home = 0;
41114402Sru  if (add_home)
42114402Sru    home = getenv("HOME");
43114402Sru  char *e = 0;
44114402Sru  if (envvar)
45114402Sru    e = getenv(envvar);
46114402Sru  dirs = new char[((e && *e) ? strlen(e) + 1 : 0)
47114402Sru		  + (add_current ? 1 + 1 : 0)
48114402Sru		  + ((home && *home) ? strlen(home) + 1 : 0)
49114402Sru		  + ((standard && *standard) ? strlen(standard) : 0)
50114402Sru		  + 1];
51114402Sru  *dirs = '\0';
52114402Sru  if (e && *e) {
53114402Sru    strcat(dirs, e);
54114402Sru    strcat(dirs, PATH_SEP);
55114402Sru  }
56114402Sru  if (add_current) {
57114402Sru    strcat(dirs, ".");
58114402Sru    strcat(dirs, PATH_SEP);
59114402Sru  }
60114402Sru  if (home && *home) {
61114402Sru    strcat(dirs, home);
62114402Sru    strcat(dirs, PATH_SEP);
63114402Sru  }
64114402Sru  if (standard && *standard)
65114402Sru    strcat(dirs, standard);
66114402Sru  init_len = strlen(dirs);
67114402Sru}
68114402Sru
69114402Srusearch_path::~search_path()
70114402Sru{
71114402Sru  // dirs is always allocated
72114402Sru  a_delete dirs;
73114402Sru}
74114402Sru
75114402Sruvoid search_path::command_line_dir(const char *s)
76114402Sru{
77114402Sru  char *old = dirs;
78114402Sru  unsigned old_len = strlen(old);
79114402Sru  unsigned slen = strlen(s);
80114402Sru  dirs = new char[old_len + 1 + slen + 1];
81114402Sru  memcpy(dirs, old, old_len - init_len);
82114402Sru  char *p = dirs;
83114402Sru  p += old_len - init_len;
84114402Sru  if (init_len == 0)
85151497Sru    *p++ = PATH_SEP_CHAR;
86114402Sru  memcpy(p, s, slen);
87114402Sru  p += slen;
88114402Sru  if (init_len > 0) {
89151497Sru    *p++ = PATH_SEP_CHAR;
90114402Sru    memcpy(p, old + old_len - init_len, init_len);
91114402Sru    p += init_len;
92114402Sru  }
93114402Sru  *p++ = '\0';
94114402Sru  a_delete old;
95114402Sru}
96114402Sru
97114402SruFILE *search_path::open_file(const char *name, char **pathp)
98114402Sru{
99114402Sru  assert(name != 0);
100114402Sru  if (IS_ABSOLUTE(name) || *dirs == '\0') {
101114402Sru    FILE *fp = fopen(name, "r");
102114402Sru    if (fp) {
103114402Sru      if (pathp)
104114402Sru	*pathp = strsave(name);
105114402Sru      return fp;
106114402Sru    }
107114402Sru    else
108114402Sru      return 0;
109114402Sru  }
110114402Sru  unsigned namelen = strlen(name);
111114402Sru  char *p = dirs;
112114402Sru  for (;;) {
113151497Sru    char *end = strchr(p, PATH_SEP_CHAR);
114114402Sru    if (!end)
115114402Sru      end = strchr(p, '\0');
116114402Sru    int need_slash = end > p && strchr(DIR_SEPS, end[-1]) == 0;
117151497Sru    char *origpath = new char[(end - p) + need_slash + namelen + 1];
118151497Sru    memcpy(origpath, p, end - p);
119114402Sru    if (need_slash)
120151497Sru      origpath[end - p] = '/';
121151497Sru    strcpy(origpath + (end - p) + need_slash, name);
122114402Sru#if 0
123151497Sru    fprintf(stderr, "origpath `%s'\n", origpath);
124151497Sru#endif
125151497Sru    char *path = relocate(origpath);
126151497Sru    a_delete origpath;
127151497Sru#if 0
128114402Sru    fprintf(stderr, "trying `%s'\n", path);
129114402Sru#endif
130114402Sru    FILE *fp = fopen(path, "r");
131114402Sru    if (fp) {
132114402Sru      if (pathp)
133114402Sru	*pathp = path;
134114402Sru      else
135114402Sru	a_delete path;
136114402Sru      return fp;
137114402Sru    }
138114402Sru    a_delete path;
139114402Sru    if (*end == '\0')
140114402Sru      break;
141114402Sru    p = end + 1;
142114402Sru  }
143114402Sru  return 0;
144114402Sru}
145151497Sru
146151497SruFILE *search_path::open_file_cautious(const char *name, char **pathp,
147151497Sru				      const char *mode)
148151497Sru{
149151497Sru  if (!mode)
150151497Sru    mode = "r";
151151497Sru  bool reading = (strchr(mode, 'r') != 0);
152151497Sru  if (name == 0 || strcmp(name, "-") == 0) {
153151497Sru    if (pathp)
154151497Sru      *pathp = strsave(reading ? "stdin" : "stdout");
155151497Sru    return (reading ? stdin : stdout);
156151497Sru  }
157151497Sru  if (!reading || IS_ABSOLUTE(name) || *dirs == '\0') {
158151497Sru    FILE *fp = fopen(name, mode);
159151497Sru    if (fp) {
160151497Sru      if (pathp)
161151497Sru	*pathp = strsave(name);
162151497Sru      return fp;
163151497Sru    }
164151497Sru    else
165151497Sru      return 0;
166151497Sru  }
167151497Sru  unsigned namelen = strlen(name);
168151497Sru  char *p = dirs;
169151497Sru  for (;;) {
170151497Sru    char *end = strchr(p, PATH_SEP_CHAR);
171151497Sru    if (!end)
172151497Sru      end = strchr(p, '\0');
173151497Sru    int need_slash = end > p && strchr(DIR_SEPS, end[-1]) == 0;
174151497Sru    char *origpath = new char[(end - p) + need_slash + namelen + 1];
175151497Sru    memcpy(origpath, p, end - p);
176151497Sru    if (need_slash)
177151497Sru      origpath[end - p] = '/';
178151497Sru    strcpy(origpath + (end - p) + need_slash, name);
179151497Sru#if 0
180151497Sru    fprintf(stderr, "origpath `%s'\n", origpath);
181151497Sru#endif
182151497Sru    char *path = relocate(origpath);
183151497Sru    a_delete origpath;
184151497Sru#if 0
185151497Sru    fprintf(stderr, "trying `%s'\n", path);
186151497Sru#endif
187151497Sru    FILE *fp = fopen(path, mode);
188151497Sru    if (fp) {
189151497Sru      if (pathp)
190151497Sru	*pathp = path;
191151497Sru      else
192151497Sru	a_delete path;
193151497Sru      return fp;
194151497Sru    }
195151497Sru    int err = errno;
196151497Sru    a_delete path;
197151497Sru    if (err != ENOENT)
198151497Sru    {
199151497Sru      errno = err;
200151497Sru      return 0;
201151497Sru    }
202151497Sru    if (*end == '\0')
203151497Sru      break;
204151497Sru    p = end + 1;
205151497Sru  }
206151497Sru  errno = ENOENT;
207151497Sru  return 0;
208151497Sru}
209