1 /*********************************************************************
2 * Copyright 2018, UCAR/Unidata
3 * See netcdf/COPYRIGHT file for copying and redistribution conditions.
4 *********************************************************************/
5
6 #include "config.h"
7 #include <stdlib.h>
8 #include <string.h>
9 #include <stdio.h>
10 #ifdef HAVE_UNISTD_H
11 #include <unistd.h>
12 #endif
13 #ifdef HAVE_SYS_STAT_H
14 #include <sys/stat.h>
15 #endif
16 #ifdef HAVE_FCNTL_H
17 #include <fcntl.h>
18 #endif
19 #ifdef _MSC_VER
20 #include <io.h>
21 #endif
22 #include "netcdf.h"
23 #include "ncuri.h"
24 #include "ncbytes.h"
25 #include "nclist.h"
26 #include "nclog.h"
27 #include "ncwinpath.h"
28
29 extern int mkstemp(char *template);
30
31 #define NC_MAX_PATH 4096
32
33 #define LBRACKET '['
34 #define RBRACKET ']'
35
36 /**************************************************/
37 /**
38 * Provide a hidden interface to allow utilities
39 * to check if a given path name is really an ncdap4 url.
40 * If no, return null, else return basename of the url
41 * minus any extension.
42 */
43
44 int
NC__testurl(const char * path,char ** basenamep)45 NC__testurl(const char* path, char** basenamep)
46 {
47 NCURI* uri;
48 int ok = NC_NOERR;
49 if(ncuriparse(path,&uri))
50 ok = NC_EURL;
51 else {
52 char* slash = (uri->path == NULL ? NULL : strrchr(uri->path, '/'));
53 char* dot;
54 if(slash == NULL) slash = (char*)path; else slash++;
55 slash = nulldup(slash);
56 if(slash == NULL)
57 dot = NULL;
58 else
59 dot = strrchr(slash, '.');
60 if(dot != NULL && dot != slash) *dot = '\0';
61 if(basenamep)
62 *basenamep=slash;
63 else if(slash)
64 free(slash);
65 }
66 ncurifree(uri);
67 return ok;
68 }
69
70 /* Return 1 if this machine is little endian */
71 int
NC_isLittleEndian(void)72 NC_isLittleEndian(void)
73 {
74 union {
75 unsigned char bytes[SIZEOF_INT];
76 int i;
77 } u;
78 u.i = 1;
79 return (u.bytes[0] == 1 ? 1 : 0);
80 }
81
82 char*
NC_backslashEscape(const char * s)83 NC_backslashEscape(const char* s)
84 {
85 const char* p;
86 char* q;
87 size_t len;
88 char* escaped = NULL;
89
90 len = strlen(s);
91 escaped = (char*)malloc(1+(2*len)); /* max is everychar is escaped */
92 if(escaped == NULL) return NULL;
93 for(p=s,q=escaped;*p;p++) {
94 char c = *p;
95 switch (c) {
96 case '\\':
97 case '/':
98 case '.':
99 case '@':
100 *q++ = '\\'; *q++ = '\\';
101 break;
102 default: *q++ = c; break;
103 }
104 }
105 *q = '\0';
106 return escaped;
107 }
108
109 char*
NC_backslashUnescape(const char * esc)110 NC_backslashUnescape(const char* esc)
111 {
112 size_t len;
113 char* s;
114 const char* p;
115 char* q;
116
117 if(esc == NULL) return NULL;
118 len = strlen(esc);
119 s = (char*)malloc(len+1);
120 if(s == NULL) return NULL;
121 for(p=esc,q=s;*p;) {
122 switch (*p) {
123 case '\\':
124 p++;
125 /* fall thru */
126 default: *q++ = *p++; break;
127 }
128 }
129 *q = '\0';
130 return s;
131 }
132
133 char*
NC_entityescape(const char * s)134 NC_entityescape(const char* s)
135 {
136 const char* p;
137 char* q;
138 size_t len;
139 char* escaped = NULL;
140 const char* entity;
141
142 len = strlen(s);
143 escaped = (char*)malloc(1+(6*len)); /* 6 = |'| */
144 if(escaped == NULL) return NULL;
145 for(p=s,q=escaped;*p;p++) {
146 char c = *p;
147 switch (c) {
148 case '&': entity = "&"; break;
149 case '<': entity = "<"; break;
150 case '>': entity = ">"; break;
151 case '"': entity = """; break;
152 case '\'': entity = "'"; break;
153 default : entity = NULL; break;
154 }
155 if(entity == NULL)
156 *q++ = c;
157 else {
158 len = strlen(entity);
159 memcpy(q,entity,len);
160 q+=len;
161 }
162 }
163 *q = '\0';
164 return escaped;
165 }
166
167 /**
168 Wrap mktmp and return the generated path,
169 or null if failed.
170 Base is the base file path. XXXXX is appended
171 to allow mktmp add its unique id.
172 Return the generated path.
173 */
174
175 char*
NC_mktmp(const char * base)176 NC_mktmp(const char* base)
177 {
178 int fd;
179 char* cvtpath = NULL;
180 char tmp[NC_MAX_PATH];
181 #ifdef HAVE_MKSTEMP
182 mode_t mask;
183 #endif
184
185 /* Make sure that this path conversion has been applied */
186 cvtpath = NCpathcvt(base);
187 strncpy(tmp,cvtpath,sizeof(tmp));
188 nullfree(cvtpath);
189 strncat(tmp, "XXXXXX", sizeof(tmp) - strlen(tmp) - 1);
190
191 #ifdef HAVE_MKSTEMP
192 /* Note Potential problem: old versions of this function
193 leave the file in mode 0666 instead of 0600 */
194 mask=umask(0077);
195 fd = mkstemp(tmp);
196 (void)umask(mask);
197 #else /* !HAVE_MKSTEMP */
198 {
199 #ifdef HAVE_MKTEMP
200 #ifdef _MSC_VER
201 /* Use _mktemp_s */
202 _mktemp_s(tmp,sizeof(tmp)-1);
203 #else /*!_MSC_VER*/
204 mktemp(tmp);
205 tmo[sizeof[tmp]-1] = '\0';
206 #endif
207 #else /* !HAVE_MKTEMP */
208 /* Need to simulate by using some kind of pseudo-random number */
209 {
210 int rno = rand();
211 char spid[7];
212 if(rno < 0) rno = -rno;
213 snprintf(spid,sizeof(spid),"%06d",rno);
214 strncat(tmp,spid,sizeof(tmp) - strlen(tmp) - 1);
215 }
216 #endif /* HAVE_MKTEMP */
217 #ifdef _MSC_VER
218 fd=NCopen3(tmp,O_RDWR|O_BINARY|O_CREAT, _S_IREAD|_S_IWRITE);
219 #else
220 fd=NCopen3(tmp,O_RDWR|O_CREAT|O_EXCL, S_IRWXU);
221 #endif
222 }
223 #endif /* !HAVE_MKSTEMP */
224 if(fd < 0) {
225 nclog(NCLOGERR, "Could not create temp file: %s",tmp);
226 return NULL;
227 } else
228 close(fd);
229 return strdup(tmp);
230 }
231
232 int
NC_readfile(const char * filename,NCbytes * content)233 NC_readfile(const char* filename, NCbytes* content)
234 {
235 int ret = NC_NOERR;
236 FILE* stream = NULL;
237 char part[1024];
238
239 #ifdef _WIN32
240 stream = NCfopen(filename,"rb");
241 #else
242 stream = NCfopen(filename,"r");
243 #endif
244 if(stream == NULL) {ret=errno; goto done;}
245 for(;;) {
246 size_t count = fread(part, 1, sizeof(part), stream);
247 if(count <= 0) break;
248 ncbytesappendn(content,part,count);
249 if(ferror(stream)) {ret = NC_EIO; goto done;}
250 if(feof(stream)) break;
251 }
252 ncbytesnull(content);
253 done:
254 if(stream) fclose(stream);
255 return ret;
256 }
257
258 int
NC_writefile(const char * filename,size_t size,void * content)259 NC_writefile(const char* filename, size_t size, void* content)
260 {
261 int ret = NC_NOERR;
262 FILE* stream = NULL;
263 void* p;
264 size_t remain;
265
266 #ifdef _WIN32
267 stream = NCfopen(filename,"wb");
268 #else
269 stream = NCfopen(filename,"w");
270 #endif
271 if(stream == NULL) {ret=errno; goto done;}
272 p = content;
273 remain = size;
274 while(remain > 0) {
275 size_t written = fwrite(p, 1, remain, stream);
276 if(ferror(stream)) {ret = NC_EIO; goto done;}
277 if(feof(stream)) break;
278 remain -= written;
279 }
280 done:
281 if(stream) fclose(stream);
282 return ret;
283 }
284
285 /*
286 Parse a path as a url and extract the modelist.
287 If the path is not a URL, then return a NULL list.
288 If a URL, but modelist is empty or does not exist,
289 then return empty list.
290 */
291 int
NC_getmodelist(const char * path,NClist ** modelistp)292 NC_getmodelist(const char* path, NClist** modelistp)
293 {
294 int stat=NC_NOERR;
295 NClist* modelist = NULL;
296 NCURI* uri = NULL;
297 const char* modestr = NULL;
298 const char* p = NULL;
299 const char* endp = NULL;
300
301 ncuriparse(path,&uri);
302 if(uri == NULL) goto done; /* not a uri */
303
304 /* Get the mode= arg from the fragment */
305 modelist = nclistnew();
306 modestr = ncurilookup(uri,"mode");
307 if(modestr == NULL || strlen(modestr) == 0) goto done;
308 /* Parse the mode string at the commas or EOL */
309 p = modestr;
310 for(;;) {
311 char* s;
312 ptrdiff_t slen;
313 endp = strchr(p,',');
314 if(endp == NULL) endp = p + strlen(p);
315 slen = (endp - p);
316 if((s = malloc(slen+1)) == NULL) {stat = NC_ENOMEM; goto done;}
317 memcpy(s,p,slen);
318 s[slen] = '\0';
319 nclistpush(modelist,s);
320 if(*endp == '\0') break;
321 p = endp+1;
322 }
323
324 done:
325 if(stat == NC_NOERR) {
326 if(modelistp) {*modelistp = modelist; modelist = NULL;}
327 }
328 ncurifree(uri);
329 nclistfree(modelist);
330 return stat;
331 }
332
333 /*
334 Check "mode=" list for a path and return 1 if present, 0 otherwise.
335 */
336 int
NC_testmode(const char * path,const char * tag)337 NC_testmode(const char* path, const char* tag)
338 {
339 int stat = NC_NOERR;
340 int found = 0;
341 int i;
342 NClist* modelist = NULL;
343
344 if((stat = NC_getmodelist(path, &modelist))) goto done;
345 for(i=0;i<nclistlength(modelist);i++) {
346 const char* value = nclistget(modelist,i);
347 if(strcasecmp(tag,value)==0) {found = 1; break;}
348 }
349
350 done:
351 nclistfreeall(modelist);
352 return found;
353 }
354