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
6 This file is part of groff.
7
8 groff is free software; you can redistribute it and/or modify it under
9 the terms of the GNU General Public License as published by the Free
10 Software Foundation; either version 2, or (at your option) any later
11 version.
12
13 groff is distributed in the hope that it will be useful, but WITHOUT ANY
14 WARRANTY; without even the implied warranty of MERCHANTABILITY or
15 FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
16 for more details.
17
18 You should have received a copy of the GNU General Public License along
19 with groff; see the file COPYING. If not, write to the Free Software
20 Foundation, 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
search_path(const char * envvar,const char * standard,int add_home,int add_current)37 search_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
~search_path()69 search_path::~search_path()
70 {
71 // dirs is always allocated
72 a_delete dirs;
73 }
74
command_line_dir(const char * s)75 void 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
open_file(const char * name,char ** pathp)97 FILE *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
open_file_cautious(const char * name,char ** pathp,const char * mode)146 FILE *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