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