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