1 // Ancillary.cc
2
3 #include "config.h"
4
5 //#define DODS_DEBUG
6
7 #include "Ancillary.h"
8 #include "debug.h"
9
10 #ifndef WIN32
11 #ifdef HAVE_UNISTD_H
12 #include <unistd.h>
13 #endif
14 #else
15 #include <io.h>
16 #include <fcntl.h>
17 #include <process.h>
18 // Win32 does not define this. 08/21/02 jhrg
19 #define F_OK 0
20 #endif
21
22 namespace libdap {
23
24 /** This function accepts a dataset path name, and searches for a
25 matching ancillary data file name with a very specific set of
26 search rules, given here:
27
28 <pre>
29 directory filename extension
30 same same `.'given
31 given same `.'given
32 same given `.'given
33 given given `.'given
34 </pre>
35
36 Where ``same'' refers to the input dataset pathname, and ``given''
37 refers to the function arguments.
38
39 For example, If you call this function with a
40 dataset name of <tt>/a/data</tt>, an extension of <tt>das</tt>, a
41 directory of
42 <tt>b</tt>, and a filename of <tt>ralph</tt>, the function will
43 look (in order)
44 for the following files:
45
46 <pre>
47 /a/data.das
48 /b/data.das
49 /a/ralph.das
50 /b/ralph.das
51 </pre>
52
53 The function will return a string containing the name of the first
54 file in the list that exists, if any.
55
56 @note This code now checks for <code>pathname.ext</code> 3/17/99 jhrg
57
58 @brief Find a file with ancillary data.
59 @param pathname The input pathname of a dataset.
60 @param ext The input extension the desired file is to have.
61 @param dir The input directory in which the desired file may be
62 found.
63 @param file The input filename the desired file may have.
64 @return A string containing the pathname of the file found by
65 searching with the given components. If no file was found, the
66 null string is returned.
67 */
68 string
find_ancillary_file(const string & pathname,const string & ext,const string & dir,const string & file)69 Ancillary::find_ancillary_file( const string &pathname,
70 const string &ext,
71 const string &dir,
72 const string &file )
73 {
74 string::size_type slash = pathname.rfind('/') + 1;
75 string directory = pathname.substr(0, slash);
76 string filename = pathname.substr(slash);
77 string basename = pathname.substr(slash, pathname.rfind('.') - slash);
78
79 DBG(cerr << "find ancillary file params: " << pathname << ", " << ext
80 << ", " << dir << ", " << file << endl);
81 DBG(cerr << "find ancillary file comp: " << directory << ", " << filename
82 << ", " << basename << endl);
83
84 string dot_ext = "." + ext;
85
86 string name = directory + basename + dot_ext;
87 if (access(name.c_str(), F_OK) == 0)
88 return name;
89
90 name = pathname + dot_ext;
91 if (access(name.c_str(), F_OK) == 0)
92 return name;
93
94 name = directory + ext;
95 if (access(name.c_str(), F_OK) == 0)
96 return name;
97
98 name = dir + basename + dot_ext;
99 if (access(name.c_str(), F_OK) == 0)
100 return name;
101
102 name = directory + file + dot_ext;
103 if (access(name.c_str(), F_OK) == 0)
104 return name;
105
106 name = dir + file + dot_ext;
107 if (access(name.c_str(), F_OK) == 0)
108 return name;
109
110 name = dir + ext;
111 if (access(name.c_str(), F_OK) == 0)
112 return name;
113
114 return "";
115 }
116
117 // Given a pathname to a datafile, take that pathname apart and look for an
118 // ancillary file that describes a group of datafiles of which this datafile
119 // is a member. Assume that groups follow a simple naming convention where
120 // files use either leading or trailing digits and a common basename to name
121 // group members. For example, 00stuff.hdf, 01stuff.hdf, 02stuff.hdf, ..., is
122 // a group and is has `stuff' as its basename.
123
124 /** Assume that <tt>name</tt> refers to a file that is one of a
125 group of files which share a common `base' name and differ only by
126 some prefix or suffix digits (e.g. <tt>00base</tt>, <tt>01base</tt>,
127 ... or <tt>base00</tt>, ... have the base name <tt>base</tt>). This
128 function looks for a file <tt>base.ext</tt>.
129
130 @param name The name (full or relative) to one member of a group
131 of files.
132 @param ext The extension of the group's ancillary file. Note that
133 <tt>ext</tt> should include a period (.) if that needs to
134 separate the base name from the extension.
135 @return The pathname to the group's ancillary file if found, otherwise
136 the empty string (""). */
137 string
find_group_ancillary_file(const string & name,const string & ext)138 Ancillary::find_group_ancillary_file( const string &name, const string &ext )
139 {
140 // Given /usr/local/data/stuff.01.nc
141 // pathname = /usr/local/data, filename = stuff.01.nc and
142 // rootname = stuff.01
143 string::size_type slash = name.find_last_of('/');
144 string dirname = name.substr(0, slash);
145 string filename = name.substr(slash + 1);
146 string rootname = filename.substr(0, filename.find_last_of('.'));
147
148 // Instead of using regexs, scan the filename for leading and then
149 // trailing digits.
150 string::iterator rootname_iter = rootname.begin();
151 string::iterator rootname_end_iter = rootname.end();
152 if (isdigit(*rootname_iter)) {
153 while (rootname_iter != rootname_end_iter
154 && isdigit(*++rootname_iter))
155 ;
156
157 // We want: new_name = dirname + "/" + <base> + ext but without
158 // creating a bunch of temp objects.
159 string new_name = dirname;
160 new_name.append("/");
161 new_name.append(rootname_iter, rootname_end_iter);
162 new_name.append(ext);
163 DBG(cerr << "New Name (iter): " << new_name << endl);
164 if (access(new_name.c_str(), F_OK) == 0) {
165 return new_name;
166 }
167 }
168
169 string::reverse_iterator rootname_riter = rootname.rbegin();
170 string::reverse_iterator rootname_end_riter = rootname.rend();
171 if (isdigit(*rootname_riter)) {
172 while (rootname_riter != rootname_end_riter
173 && isdigit(*++rootname_riter))
174 ;
175 string new_name = dirname;
176 new_name.append("/");
177 // I used reverse iters to scan rootname backwards. To avoid
178 // reversing the fragment between end_riter and riter, pass append
179 // regular iters obtained using reverse_iterator::base(). See Meyers
180 // p. 123. 1/22/2002 jhrg
181 new_name.append(rootname_end_riter.base(), rootname_riter.base());
182 new_name.append(ext);
183 DBG(cerr << "New Name (riter): " << new_name << endl);
184 if (access(new_name.c_str(), F_OK) == 0) {
185 return new_name;
186 }
187 }
188
189 // If we're here either the file does not begin with leading digits or a
190 // template made by removing those digits was not found.
191
192 return "";
193 }
194
195 void
read_ancillary_das(DAS & das,const string & pathname,const string & dir,const string & file)196 Ancillary::read_ancillary_das( DAS &das,
197 const string &pathname,
198 const string &dir,
199 const string &file )
200 {
201 string name = find_ancillary_file( pathname, "das", dir, file ) ;
202
203 DBG(cerr << "In Ancillary::read_ancillary_dds: name:" << name << endl);
204
205 FILE *in = fopen( name.c_str(), "r" ) ;
206 if( in ) {
207 das.parse( in ) ;
208 (void)fclose( in ) ;
209 #if 0
210 int res = fclose( in ) ;
211 if( res )
212 DBG(cerr << "Ancillary::read_ancillary_das - Failed to close file " << (void *)in << endl) ;
213 #endif
214 }
215 }
216
217 void
read_ancillary_dds(DDS & dds,const string & pathname,const string & dir,const string & file)218 Ancillary::read_ancillary_dds( DDS &dds,
219 const string &pathname,
220 const string &dir,
221 const string &file )
222 {
223 string name = find_ancillary_file( pathname, "dds", dir, file ) ;
224
225 DBG(cerr << "In Ancillary::read_ancillary_dds: name:" << name << endl);
226
227 FILE *in = fopen( name.c_str(), "r" ) ;
228 if( in ) {
229 dds.parse( in ) ;
230 (void)fclose( in ) ;
231 #if 0
232 int res = fclose( in ) ;
233 if( res )
234 DBG(cerr << "Ancillary::read_ancillary_das - Failed to close file " << (void *)in << endl) ;
235 #endif
236 }
237 }
238
239 } // namespace libdap
240
241