Update to groff 1.19.2.
[dragonfly.git] / contrib / groff-1.19 / src / libs / libgroff / searchpath.cpp
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
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
69 search_path::~search_path()
70 {
71   // dirs is always allocated
72   a_delete dirs;
73 }
74
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
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
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 }