904 lines
30 KiB
C
904 lines
30 KiB
C
|
/*------------------------------------------------------------------------------
|
||
|
* download.c : gnss data downloader
|
||
|
*
|
||
|
* Copyright (C) 2012-2020 by T.TAKASU, All rights reserved.
|
||
|
*
|
||
|
* version : $Revision:$ $Date:$
|
||
|
* history : 2012/12/28 1.0 new
|
||
|
* 2013/06/02 1.1 replace S_IREAD by S_IRUSR
|
||
|
* 2020/11/30 1.2 support protocol https:// and ftps://
|
||
|
* support compressed RINEX (CRX) files
|
||
|
* support wild-card (*) in URL for FTP and FTPS
|
||
|
* use "=" to separate file name from URL
|
||
|
* fix bug on double-free of download paths
|
||
|
* limit max number of download paths
|
||
|
* use integer types in stdint.h
|
||
|
*-----------------------------------------------------------------------------*/
|
||
|
#include <errno.h>
|
||
|
#include <sys/stat.h>
|
||
|
#include <sys/types.h>
|
||
|
|
||
|
#include "rtklib.h"
|
||
|
|
||
|
#define FTP_CMD "wget" /* FTP/HTTP command */
|
||
|
#define FTP_TIMEOUT 60 /* FTP/HTTP timeout (s) */
|
||
|
#define FTP_LISTING ".listing" /* FTP listing file */
|
||
|
#define FTP_NOFILE 2048 /* FTP error no file */
|
||
|
#define HTTP_NOFILE 1 /* HTTP error no file */
|
||
|
#define FTP_RETRY 3 /* FTP number of retry */
|
||
|
#define MAX_PATHS 131072 /* max number of download paths */
|
||
|
|
||
|
/* type definitions ----------------------------------------------------------*/
|
||
|
|
||
|
typedef struct { /* download path type */
|
||
|
char *remot; /* remote path */
|
||
|
char *local; /* local path */
|
||
|
} path_t;
|
||
|
|
||
|
typedef struct { /* download paths type */
|
||
|
path_t *path; /* download paths */
|
||
|
int n,nmax; /* number and max number of paths */
|
||
|
} paths_t;
|
||
|
|
||
|
/* execute command with test timeout -----------------------------------------*/
|
||
|
extern int execcmd_to(const char *cmd)
|
||
|
{
|
||
|
#ifdef WIN32
|
||
|
PROCESS_INFORMATION info;
|
||
|
STARTUPINFO si={0};
|
||
|
DWORD stat;
|
||
|
char cmds[4096];
|
||
|
|
||
|
si.cb=sizeof(si);
|
||
|
sprintf(cmds,"cmd /c %s",cmd);
|
||
|
if (!CreateProcess(NULL,(LPTSTR)cmds,NULL,NULL,FALSE,CREATE_NO_WINDOW,NULL,
|
||
|
NULL,&si,&info)) return -1;
|
||
|
|
||
|
while (WaitForSingleObject(info.hProcess,10)==WAIT_TIMEOUT) {
|
||
|
showmsg("");
|
||
|
}
|
||
|
if (!GetExitCodeProcess(info.hProcess,&stat)) stat=-1;
|
||
|
CloseHandle(info.hProcess);
|
||
|
CloseHandle(info.hThread);
|
||
|
return (int)stat;
|
||
|
#else
|
||
|
return system(cmd);
|
||
|
#endif
|
||
|
}
|
||
|
/* generate path by replacing keywords ---------------------------------------*/
|
||
|
static void genpath(const char *file, const char *name, gtime_t time, int seqno,
|
||
|
char *path)
|
||
|
{
|
||
|
char buff[1024],*p,*q,*r,*env,var[1024]="";
|
||
|
char l_name[1024]="",u_name[1024]="";
|
||
|
|
||
|
for (p=l_name,q=(char *)name;(*p=(char)tolower(*q));p++,q++) ;
|
||
|
for (p=u_name,q=(char *)name;(*p=(char)toupper(*q));p++,q++) ;
|
||
|
|
||
|
for (p=buff,q=(char *)file;(*p=*q);p++,q++) {
|
||
|
if (*q=='%') q++; else continue;
|
||
|
if (*q=='s'||*q=='r') p+=sprintf(p,"%s",l_name)-1;
|
||
|
else if (*q=='S'||*q=='R') p+=sprintf(p,"%s",u_name)-1;
|
||
|
else if (*q=='N') p+=sprintf(p,"%d",seqno)-1;
|
||
|
else if (*q=='{'&&(r=strchr(q+1,'}'))) {
|
||
|
strncpy(var,q+1,r-q-1);
|
||
|
var[r-q-1]='\0';
|
||
|
if ((env=getenv(var))) p+=sprintf(p,"%s",env)-1;
|
||
|
q=r;
|
||
|
}
|
||
|
else q--;
|
||
|
}
|
||
|
reppath(buff,path,time,"","");
|
||
|
}
|
||
|
/* parse field strings separated by spaces -----------------------------------*/
|
||
|
static char *parse_str(char *buff, char *str, int nmax)
|
||
|
{
|
||
|
char *p,*q,sep[]=" \r\n";
|
||
|
|
||
|
for (p=buff;*p&&*p==' ';p++) ;
|
||
|
|
||
|
if (*p=='"') sep[0]=*p++; /* enclosed within quotation marks */
|
||
|
|
||
|
for (q=str;*p&&!strchr(sep,*p);p++) {
|
||
|
if (q<str+nmax-1) *q++=*p;
|
||
|
}
|
||
|
*q='\0';
|
||
|
return *p?p+1:p;
|
||
|
}
|
||
|
/* compare str1 and str2 with wildcards (*) ----------------------------------*/
|
||
|
static int cmp_str(const char *str1, const char *str2)
|
||
|
{
|
||
|
char s1[1026],s2[1026],*p,*q;
|
||
|
|
||
|
sprintf(s1,"^%s$",str1);
|
||
|
sprintf(s2,"^%s$",str2);
|
||
|
|
||
|
for (p=s1,q=strtok(s2,"*");q;q=strtok(NULL,"*")) {
|
||
|
if ((p=strstr(p,q))) p+=strlen(q); else break;
|
||
|
}
|
||
|
return p!=NULL;
|
||
|
}
|
||
|
/* remote to local file path -------------------------------------------------*/
|
||
|
static void remot2local(const char *remot, const char *dir, char *local)
|
||
|
{
|
||
|
char *p;
|
||
|
|
||
|
if ((p=strrchr(remot,'='))) p++;
|
||
|
else if ((p=strrchr(remot,'/'))) p++;
|
||
|
else p=(char *)remot;
|
||
|
|
||
|
sprintf(local,"%s%c%s",dir,FILEPATHSEP,p);
|
||
|
}
|
||
|
/* test file existence -------------------------------------------------------*/
|
||
|
static int exist_file(const char *local)
|
||
|
{
|
||
|
#ifdef WIN32
|
||
|
DWORD stat=GetFileAttributes(local);
|
||
|
return stat!=0xFFFFFFFF;
|
||
|
#else
|
||
|
struct stat buff;
|
||
|
if (stat(local,&buff)) return 0;
|
||
|
return buff.st_mode&S_IRUSR;
|
||
|
#endif
|
||
|
}
|
||
|
/* test local file existence -------------------------------------------------*/
|
||
|
static int test_file(const char *local)
|
||
|
{
|
||
|
char buff[1024],*p;
|
||
|
int comp=0;
|
||
|
|
||
|
if (strchr(local,'*')) { /* test wild-card (*) in path */
|
||
|
return 0;
|
||
|
}
|
||
|
strcpy(buff,local);
|
||
|
if ((p=strrchr(buff,'.'))&&
|
||
|
(!strcmp(p,".z")||!strcmp(p,".gz")||!strcmp(p,".zip")||
|
||
|
!strcmp(p,".Z")||!strcmp(p,".GZ")||!strcmp(p,".ZIP"))) {
|
||
|
*p='\0';
|
||
|
if (exist_file(buff)) return 1;
|
||
|
comp=1;
|
||
|
}
|
||
|
if ((p=strrchr(buff,'.'))&&strlen(p)==4&&(*(p+3)=='d'||*(p+3)=='D')) {
|
||
|
*(p+3)=*(p+3)=='d'?'o':'O';
|
||
|
if (exist_file(buff)) return 1;
|
||
|
comp=1;
|
||
|
}
|
||
|
if ((p=strrchr(buff,'.'))&&(!strcmp(p,".crx")||!strcmp(p,".CRX"))) {
|
||
|
if (!strcmp(p,".crx")) strcpy(p,".cro"); else strcpy(p,".CRO");
|
||
|
if (exist_file(buff)) return 1;
|
||
|
comp=1;
|
||
|
}
|
||
|
if (!exist_file(buff)) return 0;
|
||
|
return comp?2:1;
|
||
|
}
|
||
|
/* free download paths -------------------------------------------------------*/
|
||
|
static void free_path(paths_t *paths)
|
||
|
{
|
||
|
int i;
|
||
|
if (!paths) return;
|
||
|
for (i=0;i<paths->n;i++) {
|
||
|
free(paths->path[i].remot);
|
||
|
free(paths->path[i].local);
|
||
|
}
|
||
|
free(paths->path);
|
||
|
}
|
||
|
/* add download paths --------------------------------------------------------*/
|
||
|
static int add_path(paths_t *paths, const char *remot, const char *dir)
|
||
|
{
|
||
|
path_t *paths_path;
|
||
|
char local[1024];
|
||
|
|
||
|
if (paths->n>=paths->nmax) {
|
||
|
if ((paths->nmax=paths->nmax<=0?1024:paths->nmax*2)>MAX_PATHS) {
|
||
|
return 0;
|
||
|
}
|
||
|
paths_path=(path_t *)realloc(paths->path,sizeof(path_t)*paths->nmax);
|
||
|
if (!paths_path) {
|
||
|
return 0;
|
||
|
}
|
||
|
paths->path=paths_path;
|
||
|
}
|
||
|
remot2local(remot,dir,local);
|
||
|
|
||
|
paths->path[paths->n].remot=paths->path[paths->n].local=NULL;
|
||
|
|
||
|
if (!(paths->path[paths->n].remot=(char *)malloc(strlen(remot)+1))||
|
||
|
!(paths->path[paths->n].local=(char *)malloc(strlen(local)+1))) {
|
||
|
return 0;
|
||
|
}
|
||
|
strcpy(paths->path[paths->n].remot,remot);
|
||
|
strcpy(paths->path[paths->n].local,local);
|
||
|
paths->n++;
|
||
|
return 1;
|
||
|
}
|
||
|
/* generate download path ----------------------------------------------------*/
|
||
|
static int gen_path(gtime_t time, gtime_t time_p, int seqnos, int seqnoe,
|
||
|
const url_t *url, const char *sta, const char *dir,
|
||
|
paths_t *paths)
|
||
|
{
|
||
|
char remot[1024],remot_p[1024],dir_t[1024];
|
||
|
int i;
|
||
|
|
||
|
if (!*dir) dir=url->dir;
|
||
|
if (!*dir) dir=".";
|
||
|
|
||
|
if (strstr(url->path,"%N")) {
|
||
|
for (i=seqnos;i<=seqnoe;i++) {
|
||
|
genpath(url->path,sta,time,i,remot);
|
||
|
genpath(dir ,sta,time,i,dir_t);
|
||
|
if (time_p.time) {
|
||
|
genpath(url->path,sta,time_p,i,remot_p);
|
||
|
if (!strcmp(remot_p,remot)) continue;
|
||
|
}
|
||
|
if (!add_path(paths,remot,dir_t)) return 0;
|
||
|
}
|
||
|
}
|
||
|
else {
|
||
|
genpath(url->path,sta,time,0,remot);
|
||
|
genpath(dir ,sta,time,0,dir_t);
|
||
|
if (time_p.time) {
|
||
|
genpath(url->path,sta,time_p,0,remot_p);
|
||
|
if (!strcmp(remot_p,remot)) return 1;
|
||
|
}
|
||
|
if (!add_path(paths,remot,dir_t)) return 0;
|
||
|
}
|
||
|
return 1;
|
||
|
}
|
||
|
/* generate download paths ---------------------------------------------------*/
|
||
|
static int gen_paths(gtime_t time, gtime_t time_p, int seqnos, int seqnoe,
|
||
|
const url_t *url, char **stas, int nsta, const char *dir,
|
||
|
paths_t *paths)
|
||
|
{
|
||
|
int i;
|
||
|
|
||
|
if (strstr(url->path,"%s")||strstr(url->path,"%S")) {
|
||
|
for (i=0;i<nsta;i++) {
|
||
|
if (!gen_path(time,time_p,seqnos,seqnoe,url,stas[i],dir,paths)) {
|
||
|
return 0;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
else {
|
||
|
if (!gen_path(time,time_p,seqnos,seqnoe,url,"",dir,paths)) {
|
||
|
return 0;
|
||
|
}
|
||
|
}
|
||
|
return 1;
|
||
|
}
|
||
|
/* compact download paths ----------------------------------------------------*/
|
||
|
static void compact_paths(paths_t *paths)
|
||
|
{
|
||
|
int i,j,k;
|
||
|
|
||
|
for (i=0;i<paths->n;i++) {
|
||
|
for (j=i+1;j<paths->n;j++) {
|
||
|
if (strcmp(paths->path[i].remot,paths->path[j].remot)) continue;
|
||
|
free(paths->path[j].remot);
|
||
|
free(paths->path[j].local);
|
||
|
for (k=j;k<paths->n-1;k++) paths->path[k]=paths->path[k+1];
|
||
|
paths->n--; j--;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
/* generate local directory recursively --------------------------------------*/
|
||
|
static int mkdir_r(const char *dir)
|
||
|
{
|
||
|
char pdir[1024],*p;
|
||
|
|
||
|
#ifdef WIN32
|
||
|
HANDLE h;
|
||
|
WIN32_FIND_DATA data;
|
||
|
|
||
|
if (!*dir||!strcmp(dir+1,":\\")) return 1;
|
||
|
|
||
|
strcpy(pdir,dir);
|
||
|
if ((p=strrchr(pdir,FILEPATHSEP))) {
|
||
|
*p='\0';
|
||
|
h=FindFirstFile(pdir,&data);
|
||
|
if (h==INVALID_HANDLE_VALUE) {
|
||
|
if (!mkdir_r(pdir)) return 0;
|
||
|
}
|
||
|
else FindClose(h);
|
||
|
}
|
||
|
if (CreateDirectory(dir,NULL)||
|
||
|
GetLastError()==ERROR_ALREADY_EXISTS) return 1;
|
||
|
|
||
|
trace(2,"directory generation error: dir=%s\n",dir);
|
||
|
return 0;
|
||
|
#else
|
||
|
FILE *fp;
|
||
|
|
||
|
if (!*dir) return 1;
|
||
|
|
||
|
strcpy(pdir,dir);
|
||
|
if ((p=strrchr(pdir,FILEPATHSEP))) {
|
||
|
*p='\0';
|
||
|
if (!(fp=fopen(pdir,"r"))) {
|
||
|
if (!mkdir_r(pdir)) return 0;
|
||
|
}
|
||
|
else fclose(fp);
|
||
|
}
|
||
|
if (!mkdir(dir,0777)||errno==EEXIST) return 1;
|
||
|
|
||
|
trace(2,"directory generation error: dir=%s\n",dir);
|
||
|
return 0;
|
||
|
#endif
|
||
|
}
|
||
|
/* get remote file list for FTP or FTPS --------------------------------------*/
|
||
|
static int get_list(const path_t *path, const char *usr, const char *pwd,
|
||
|
const char *proxy)
|
||
|
{
|
||
|
FILE *fp;
|
||
|
char cmd[4096],env[1024]="",remot[1024],*opt="",*opt2="",*p;
|
||
|
int stat;
|
||
|
|
||
|
#ifndef WIN32
|
||
|
opt2=" -o /dev/null";
|
||
|
#endif
|
||
|
remove(FTP_LISTING);
|
||
|
|
||
|
strcpy(remot,path->remot);
|
||
|
|
||
|
if ((p=strrchr(remot,'/'))) strcpy(p+1,"__REQUEST_LIST__"); else return 0;
|
||
|
|
||
|
if (*proxy) {
|
||
|
sprintf(env,"set ftp_proxy=http://%s & ",proxy);
|
||
|
opt="--proxy=on ";
|
||
|
}
|
||
|
sprintf(cmd,"%s%s %s --ftp-user=%s --ftp-password=%s --glob=off "
|
||
|
"--passive-ftp --no-remove-listing -N %s-t 1 -T %d%s\n",
|
||
|
env,FTP_CMD,remot,usr,pwd,opt,FTP_TIMEOUT,opt2);
|
||
|
execcmd_to(cmd);
|
||
|
|
||
|
if (!(fp=fopen(FTP_LISTING,"r"))) return 0;
|
||
|
fclose(fp);
|
||
|
return 1;
|
||
|
}
|
||
|
/* replace wild-card (*) in the paths ----------------------------------------*/
|
||
|
static int rep_paths(path_t *path, const char *file)
|
||
|
{
|
||
|
char buff1[1024],buff2[1024],*p,*q,*remot,*local;
|
||
|
|
||
|
strcpy(buff1,path->remot);
|
||
|
strcpy(buff2,path->local);
|
||
|
if ((p=strrchr(buff1,'/'))) p++; else p=buff1;
|
||
|
if ((q=strrchr(buff2,FILEPATHSEP))) q++; else q=buff2;
|
||
|
strcpy(p,file);
|
||
|
strcpy(q,file);
|
||
|
|
||
|
if (!(remot=(char *)malloc(strlen(buff1)+1)) ||
|
||
|
!(local=(char *)malloc(strlen(buff2)+1))) {
|
||
|
free(remot);
|
||
|
return 0;
|
||
|
}
|
||
|
strcpy(remot,buff1);
|
||
|
strcpy(local,buff2);
|
||
|
free(path->remot);
|
||
|
free(path->local);
|
||
|
path->remot=remot;
|
||
|
path->local=local;
|
||
|
return 1;
|
||
|
}
|
||
|
/* test file in remote file list ---------------------------------------------*/
|
||
|
static int test_list(path_t *path)
|
||
|
{
|
||
|
FILE *fp;
|
||
|
char buff[1024],*file,*list,*p;
|
||
|
int i;
|
||
|
|
||
|
if (!(fp=fopen(FTP_LISTING,"r"))) return 1;
|
||
|
|
||
|
if ((p=strrchr(path->remot,'/'))) file=p+1; else return 1;
|
||
|
|
||
|
/* search file in remote file list */
|
||
|
while (fgets(buff,sizeof(buff),fp)) {
|
||
|
|
||
|
/* remove symbolic link */
|
||
|
if ((p=strstr(buff,"->"))) *p='\0';
|
||
|
|
||
|
for (i=strlen(buff)-1;i>=0;i--) {
|
||
|
if (strchr(" \r\n",buff[i])) buff[i]='\0'; else break;
|
||
|
}
|
||
|
/* file as last field */
|
||
|
if ((p=strrchr(buff,' '))) list=p+1; else list=buff;
|
||
|
|
||
|
if (!strcmp(file,list)) {
|
||
|
fclose(fp);
|
||
|
return 1;
|
||
|
}
|
||
|
/* compare with wild-card (*) */
|
||
|
if (cmp_str(list,file)) {
|
||
|
|
||
|
/* replace wild-card (*) in the paths */
|
||
|
if (!rep_paths(path,list)) {
|
||
|
fclose(fp);
|
||
|
return 0;
|
||
|
}
|
||
|
fclose(fp);
|
||
|
return 1;
|
||
|
}
|
||
|
}
|
||
|
fclose(fp);
|
||
|
return 0;
|
||
|
}
|
||
|
/* execute download ----------------------------------------------------------*/
|
||
|
static int exec_down(path_t *path, char *remot_p, const char *usr,
|
||
|
const char *pwd, const char *proxy, int opts, int *n,
|
||
|
FILE *fp)
|
||
|
{
|
||
|
char dir[1024],errfile[1024],tmpfile[1024],cmd[4096],env[1024]="";
|
||
|
char opt[1024]="",*opt2="",*p;
|
||
|
int ret,proto;
|
||
|
|
||
|
#ifndef WIN32
|
||
|
opt2=" 2> /dev/null";
|
||
|
#endif
|
||
|
strcpy(dir,path->local);
|
||
|
if ((p=strrchr(dir,FILEPATHSEP))) *p='\0';
|
||
|
|
||
|
if (!strncmp(path->remot,"ftp://" ,6)) proto=0;
|
||
|
else if (!strncmp(path->remot,"ftps://" ,7)) proto=2;
|
||
|
else if (!strncmp(path->remot,"http://" ,7)) proto=1;
|
||
|
else if (!strncmp(path->remot,"https://",8)) proto=1;
|
||
|
else {
|
||
|
trace(2,"exec_down: invalid path %s\n",path->remot);
|
||
|
showmsg("STAT=X");
|
||
|
if (fp) fprintf(fp,"%s ERROR (INVALID PATH)\n",path->remot);
|
||
|
n[1]++;
|
||
|
return 0;
|
||
|
}
|
||
|
/* test local file existence */
|
||
|
if (!(opts&DLOPT_FORCE)&&test_file(path->local)) {
|
||
|
showmsg("STAT=.");
|
||
|
if (fp) fprintf(fp,"%s in %s\n",path->remot,dir);
|
||
|
n[2]++;
|
||
|
return 0;
|
||
|
}
|
||
|
showmsg("STAT=_");
|
||
|
|
||
|
/* get remote file list for FTP or FTPS */
|
||
|
if ((proto==0||proto==2)&&(p=strrchr(path->remot,'/'))&&
|
||
|
strncmp(path->remot,remot_p,p-path->remot)) {
|
||
|
|
||
|
if (get_list(path,usr,pwd,proxy)) {
|
||
|
strcpy(remot_p,path->remot);
|
||
|
}
|
||
|
}
|
||
|
/* test file in listing for FTP or FTPS or extend wild-card in file path */
|
||
|
if ((proto==0||proto==2)&&!test_list(path)) {
|
||
|
showmsg("STAT=x");
|
||
|
if (fp) fprintf(fp,"%s NO_FILE\n",path->remot);
|
||
|
n[1]++;
|
||
|
return 0;
|
||
|
}
|
||
|
/* generate local directory recursively */
|
||
|
if (!mkdir_r(dir)) {
|
||
|
showmsg("STAT=X");
|
||
|
if (fp) fprintf(fp,"%s -> %s ERROR (LOCAL DIR)\n",path->remot,dir);
|
||
|
n[3]++;
|
||
|
return 0;
|
||
|
}
|
||
|
/* re-test local file existence for file with wild-card */
|
||
|
if (!(opts&DLOPT_FORCE)&&test_file(path->local)) {
|
||
|
showmsg("STAT=.");
|
||
|
if (fp) fprintf(fp,"%s in %s\n",path->remot,dir);
|
||
|
n[2]++;
|
||
|
return 0;
|
||
|
}
|
||
|
/* proxy option */
|
||
|
if (*proxy) {
|
||
|
sprintf(env,"set %s_proxy=http://%s & ",proto==0||proto==2?"ftp":"http",
|
||
|
proxy);
|
||
|
sprintf(opt," --proxy=on ");
|
||
|
}
|
||
|
/* download command */
|
||
|
sprintf(errfile,"%s.err",path->local);
|
||
|
if (proto==0||proto==2) {
|
||
|
sprintf(cmd,"%s%s %s --ftp-user=%s --ftp-password=%s --glob=off "
|
||
|
"--passive-ftp %s-t %d -T %d -O \"%s\" -o \"%s\"%s\n",
|
||
|
env,FTP_CMD,path->remot,usr,pwd,opt,FTP_RETRY,FTP_TIMEOUT,
|
||
|
path->local,errfile,opt2);
|
||
|
}
|
||
|
else {
|
||
|
if (*pwd) {
|
||
|
sprintf(opt+strlen(opt)," --http-user=%s --http-password=%s ",usr,
|
||
|
pwd);
|
||
|
}
|
||
|
sprintf(cmd,"%s%s %s %s-t %d -T %d -O \"%s\" -o \"%s\"%s\n",env,FTP_CMD,
|
||
|
path->remot,opt,FTP_RETRY,FTP_TIMEOUT,path->local,errfile,opt2);
|
||
|
}
|
||
|
if (fp) fprintf(fp,"%s -> %s",path->remot,dir);
|
||
|
|
||
|
/* execute download command */
|
||
|
if ((ret=execcmd_to(cmd))) {
|
||
|
if ((proto==0&&ret==FTP_NOFILE)||
|
||
|
(proto==1&&ret==HTTP_NOFILE)) {
|
||
|
showmsg("STAT=x");
|
||
|
if (fp) fprintf(fp," NO_FILE\n");
|
||
|
n[1]++;
|
||
|
}
|
||
|
else {
|
||
|
trace(2,"exec_down: error proto=%d %d\n",proto,ret);
|
||
|
showmsg("STAT=X");
|
||
|
if (fp) fprintf(fp," ERROR (%d)\n",ret);
|
||
|
n[3]++;
|
||
|
}
|
||
|
remove(path->local);
|
||
|
if (!(opts&DLOPT_HOLDERR)) {
|
||
|
remove(errfile);
|
||
|
}
|
||
|
return ret==2;
|
||
|
}
|
||
|
remove(errfile);
|
||
|
|
||
|
/* uncompress download file */
|
||
|
if (!(opts&DLOPT_KEEPCMP)&&(p=strrchr(path->local,'.'))&&
|
||
|
(!strcmp(p,".z")||!strcmp(p,".gz")||!strcmp(p,".zip")||
|
||
|
!strcmp(p,".Z")||!strcmp(p,".GZ")||!strcmp(p,".ZIP"))) {
|
||
|
|
||
|
if (rtk_uncompress(path->local,tmpfile)) {
|
||
|
remove(path->local);
|
||
|
}
|
||
|
else {
|
||
|
trace(2,"exec_down: uncompress error\n");
|
||
|
showmsg("STAT=C");
|
||
|
if (fp) fprintf(fp," ERROR (UNCOMP)\n");
|
||
|
n[3]++;
|
||
|
return 0;
|
||
|
}
|
||
|
}
|
||
|
showmsg("STAT=o");
|
||
|
if (fp) fprintf(fp," OK\n");
|
||
|
n[0]++;
|
||
|
return 0;
|
||
|
}
|
||
|
/* test local file -----------------------------------------------------------*/
|
||
|
static int test_local(gtime_t ts, gtime_t te, double ti, const char *path,
|
||
|
const char *sta, const char *dir, int *nc, int *nt,
|
||
|
FILE *fp)
|
||
|
{
|
||
|
gtime_t time;
|
||
|
char remot[1024],remot_p[1024],dir_t[1024],local[1024],str[1024];
|
||
|
int stat,abort=0;
|
||
|
|
||
|
for (time=ts;timediff(time,te)<=1E-3;time=timeadd(time,ti)) {
|
||
|
|
||
|
sprintf(str,"%s->%s",path,local);
|
||
|
|
||
|
if (showmsg(str)) {
|
||
|
abort=1;
|
||
|
break;
|
||
|
}
|
||
|
genpath(path,sta,time,0,remot);
|
||
|
genpath(dir ,sta,time,0,dir_t);
|
||
|
remot2local(remot,dir_t,local);
|
||
|
|
||
|
stat=test_file(local);
|
||
|
|
||
|
fprintf(fp," %s",stat==0?"-":(stat==1?"o":"z"));
|
||
|
|
||
|
showmsg("STAT=%s",stat==0?"x":(stat==1?"o":"z"));
|
||
|
|
||
|
(*nt)++; if (stat) (*nc)++;
|
||
|
}
|
||
|
fprintf(fp,"\n");
|
||
|
return abort;
|
||
|
}
|
||
|
/* test local files ----------------------------------------------------------*/
|
||
|
static int test_locals(gtime_t ts, gtime_t te, double ti, const url_t *url,
|
||
|
char **stas, int nsta, const char *dir, int *nc, int *nt,
|
||
|
FILE *fp)
|
||
|
{
|
||
|
int i;
|
||
|
|
||
|
if (strstr(url->path,"%s")||strstr(url->path,"%S")) {
|
||
|
fprintf(fp,"%s\n",url->type);
|
||
|
for (i=0;i<nsta;i++) {
|
||
|
fprintf(fp,"%-12s:",stas[i]);
|
||
|
if (test_local(ts,te,ti,url->path,stas[i],*dir?dir:url->dir,nc+i,
|
||
|
nt+i,fp)) {
|
||
|
return 1;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
else {
|
||
|
fprintf(fp,"%-12s:",url->type);
|
||
|
if (test_local(ts,te,ti,url->path,"",*dir?dir:url->dir,nc,nt,fp)) {
|
||
|
return 1;
|
||
|
}
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
/* print total count of local files ------------------------------------------*/
|
||
|
static int print_total(const url_t *url, char **stas, int nsta, int *nc,
|
||
|
int *nt, FILE *fp)
|
||
|
{
|
||
|
int i;
|
||
|
|
||
|
if (strstr(url->path,"%s")||strstr(url->path,"%S")) {
|
||
|
fprintf(fp,"%s\n",url->type);
|
||
|
for (i=0;i<nsta;i++) {
|
||
|
fprintf(fp,"%-12s: %5d/%5d\n",stas[i],nc[i],nt[i]);
|
||
|
}
|
||
|
return nsta;
|
||
|
}
|
||
|
fprintf(fp,"%-12s: %5d/%5d\n",url->type,nc[0],nt[0]);
|
||
|
return 1;
|
||
|
}
|
||
|
/* read URL list file ----------------------------------------------------------
|
||
|
* read URL list file for GNSS data
|
||
|
* args : char *file I URL list file
|
||
|
* char **types I selected types ("*":wildcard)
|
||
|
* int ntype I number of selected types
|
||
|
* urls_t *urls O URL list
|
||
|
* int nmax I max number of URL list
|
||
|
* return : number of URL addresses (0:error)
|
||
|
* notes :
|
||
|
* (1) URL list file contains records containing the following fields
|
||
|
* separated by spaces. if a field contains spaces, enclose it within "".
|
||
|
*
|
||
|
* data_type url_address default_local_directory
|
||
|
*
|
||
|
* (2) strings after # in a line are treated as comments
|
||
|
* (3) url_address should be:
|
||
|
*
|
||
|
* ftp://host_address/file_path or
|
||
|
* ftps://host_address/file_path or
|
||
|
* http://host_address/file_path or
|
||
|
* https://host_address/file_path
|
||
|
*
|
||
|
* (4) the field url_address or default_local_directory can include the
|
||
|
* follwing keywords replaced by date, time, station names and environment
|
||
|
* variables.
|
||
|
*
|
||
|
* %Y -> yyyy : year (4 digits) (2000-2099)
|
||
|
* %y -> yy : year (2 digits) (00-99)
|
||
|
* %m -> mm : month (01-12)
|
||
|
* %d -> dd : day of month (01-31)
|
||
|
* %h -> hh : hours (00-23)
|
||
|
* %H -> a : hour code (a-x)
|
||
|
* %M -> mm : minutes (00-59)
|
||
|
* %n -> ddd : day of year (001-366)
|
||
|
* %W -> wwww : gps week (0001-9999)
|
||
|
* %D -> d : day of gps week (0-6)
|
||
|
* %N -> nnn : general number
|
||
|
* %s -> ssss : station name (lower-case)
|
||
|
* %S -> SSSS : station name (upper-case)
|
||
|
* %r -> rrrr : station name
|
||
|
* %{env} -> env : environment variable
|
||
|
*-----------------------------------------------------------------------------*/
|
||
|
extern int dl_readurls(const char *file, char **types, int ntype, url_t *urls,
|
||
|
int nmax)
|
||
|
{
|
||
|
FILE *fp;
|
||
|
char buff[2048],type[32],path[1024],dir[1024],*p;
|
||
|
int i,n=0;
|
||
|
|
||
|
if (!(fp=fopen(file,"r"))) {
|
||
|
fprintf(stderr,"options file read error %s\n",file);
|
||
|
return 0;
|
||
|
}
|
||
|
for (i=0;i<ntype;i++) {
|
||
|
rewind(fp);
|
||
|
while (fgets(buff,sizeof(buff),fp)&&n<nmax) {
|
||
|
if ((p=strchr(buff,'#'))) *p='\0';
|
||
|
p=buff;
|
||
|
p=parse_str(p,type,sizeof(type));
|
||
|
p=parse_str(p,path,sizeof(path));
|
||
|
p=parse_str(p,dir ,sizeof(dir ));
|
||
|
if (!*type||!*path) continue;
|
||
|
if (!cmp_str(type,types[i])) continue;
|
||
|
strcpy(urls[n ].type,type);
|
||
|
strcpy(urls[n ].path,path);
|
||
|
strcpy(urls[n++].dir ,dir );
|
||
|
}
|
||
|
}
|
||
|
fclose(fp);
|
||
|
|
||
|
if (n<=0) {
|
||
|
fprintf(stderr,"no url in options file %s\n",file);
|
||
|
return 0;
|
||
|
}
|
||
|
return n;
|
||
|
}
|
||
|
/* read station list file ------------------------------------------------------
|
||
|
* read station list file
|
||
|
* args : char *file I station list file
|
||
|
* char **stas O stations
|
||
|
* int nmax I max number of stations
|
||
|
* return : number of stations (0:error)
|
||
|
* notes :
|
||
|
* (1) station list file contains station names separated by spaces.
|
||
|
* (2) strings after # in a line are treated as comments
|
||
|
*-----------------------------------------------------------------------------*/
|
||
|
extern int dl_readstas(const char *file, char **stas, int nmax)
|
||
|
{
|
||
|
FILE *fp;
|
||
|
char buff[4096],*p;
|
||
|
int n=0;
|
||
|
|
||
|
if (!(fp=fopen(file,"r"))) {
|
||
|
fprintf(stderr,"station list file read error %s\n",file);
|
||
|
return 0;
|
||
|
}
|
||
|
while (fgets(buff,sizeof(buff),fp)&&n<nmax) {
|
||
|
if ((p=strchr(buff,'#'))) *p='\0';
|
||
|
for (p=strtok(buff," \r\n");p&&n<nmax;p=strtok(NULL," \r\n")) {
|
||
|
strcpy(stas[n++],p);
|
||
|
}
|
||
|
}
|
||
|
fclose(fp);
|
||
|
|
||
|
if (n<=0) {
|
||
|
fprintf(stderr,"no station in station file %s\n",file);
|
||
|
return 0;
|
||
|
}
|
||
|
return n;
|
||
|
}
|
||
|
/* execute download ------------------------------------------------------------
|
||
|
* execute download
|
||
|
* args : gtime_t ts,te I time start and end
|
||
|
* double tint I time interval (s)
|
||
|
* int seqnos I sequence number start
|
||
|
* int seqnoe I sequence number end
|
||
|
* url_t *urls I URL list
|
||
|
* int nurl I number of URL list
|
||
|
* char **stas I station list
|
||
|
* int nsta I number of station list
|
||
|
* char *dir I local directory
|
||
|
* char *remote_p I previous remote file path
|
||
|
* char *usr I login user for FTP or FTPS
|
||
|
* char *pwd I login password for FTP or FTPS
|
||
|
* char *proxy I proxy server address
|
||
|
* int opts I download options (or of the followings)
|
||
|
* DLOPT_FORCE = force download existing file
|
||
|
* DLOPT_KEEPCMP=keep compressed file
|
||
|
* DLOPT_HOLDERR=hold on error file
|
||
|
* DLOPT_HOLDLST=hold on listing file
|
||
|
* char *msg O output messages
|
||
|
* FILE *fp IO log file pointer (NULL: no output log)
|
||
|
* return : status (1:ok,0:error,-1:aborted)
|
||
|
* notes : The URL list should be read by using dl_readurl()
|
||
|
* In the FTP or FTPS cases, the file name in a URL can contain wild-
|
||
|
* cards (*). The directory in a URL can not contain any wild-cards.
|
||
|
* If the file name contains wild-cards, dl_exec() gets a file-list in
|
||
|
* the remote directory and downloads the firstly matched file in the
|
||
|
* remote file-list. The secondary matched or the following files are
|
||
|
* not downloaded.
|
||
|
*-----------------------------------------------------------------------------*/
|
||
|
extern int dl_exec(gtime_t ts, gtime_t te, double ti, int seqnos, int seqnoe,
|
||
|
const url_t *urls, int nurl, char **stas, int nsta,
|
||
|
const char *dir, const char *usr, const char *pwd,
|
||
|
const char *proxy, int opts, char *msg, FILE *fp)
|
||
|
{
|
||
|
paths_t paths={0};
|
||
|
gtime_t ts_p={0};
|
||
|
char str[2048],remot_p[1024]="";
|
||
|
int i,n[4]={0};
|
||
|
uint32_t tick=tickget();
|
||
|
|
||
|
showmsg("STAT=_");
|
||
|
|
||
|
/* generate download paths */
|
||
|
while (timediff(ts,te)<1E-3) {
|
||
|
|
||
|
for (i=0;i<nurl;i++) {
|
||
|
if (!gen_paths(ts,ts_p,seqnos,seqnoe,urls+i,stas,nsta,dir,&paths)) {
|
||
|
free_path(&paths);
|
||
|
sprintf(msg,"too many download files");
|
||
|
return 0;
|
||
|
}
|
||
|
}
|
||
|
ts_p=ts; ts=timeadd(ts,ti);
|
||
|
}
|
||
|
/* compact download paths */
|
||
|
compact_paths(&paths);
|
||
|
|
||
|
if (paths.n<=0) {
|
||
|
sprintf(msg,"no download data");
|
||
|
return 0;
|
||
|
}
|
||
|
for (i=0;i<paths.n;i++) {
|
||
|
|
||
|
sprintf(str,"%s->%s (%d/%d)",paths.path[i].remot,paths.path[i].local,i+1,
|
||
|
paths.n);
|
||
|
if (showmsg(str)) break;
|
||
|
|
||
|
/* execute download */
|
||
|
if (exec_down(paths.path+i,remot_p,usr,pwd,proxy,opts,n,fp)) {
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
if (!(opts&DLOPT_HOLDLST)) {
|
||
|
remove(FTP_LISTING);
|
||
|
}
|
||
|
sprintf(msg,"OK=%d No_File=%d Skip=%d Error=%d (Time=%.1f s)",n[0],n[1],n[2],
|
||
|
n[3],(tickget()-tick)*0.001);
|
||
|
|
||
|
free_path(&paths);
|
||
|
|
||
|
return 1;
|
||
|
}
|
||
|
/* execute local file test -----------------------------------------------------
|
||
|
* execute local file test
|
||
|
* args : gtime_t ts,te I time start and end
|
||
|
* double tint I time interval (s)
|
||
|
* url_t *urls I remote URL addresses
|
||
|
* int nurl I number of remote URL addresses
|
||
|
* char **stas I stations
|
||
|
* int nsta I number of stations
|
||
|
* char *dir I local directory
|
||
|
* int ncol I number of column
|
||
|
* int datefmt I date format (0:year-dow,1:year-dd/mm,2:week)
|
||
|
* FILE *fp IO log test result file pointer
|
||
|
* return : status (1:ok,0:error,-1:aborted)
|
||
|
*-----------------------------------------------------------------------------*/
|
||
|
extern void dl_test(gtime_t ts, gtime_t te, double ti, const url_t *urls,
|
||
|
int nurl, char **stas, int nsta, const char *dir,
|
||
|
int ncol, int datefmt, FILE *fp)
|
||
|
{
|
||
|
gtime_t time;
|
||
|
double tow;
|
||
|
char year[32],date[32],date_p[32];
|
||
|
int i,j,n,m,*nc,*nt,week,flag,abort=0;
|
||
|
|
||
|
if (ncol<1) ncol=1; else if (ncol>200) ncol=200;
|
||
|
|
||
|
fprintf(fp,"** LOCAL DATA AVAILABILITY (%s, %s) **\n\n",
|
||
|
time_str(timeget(),0),*dir?dir:"*");
|
||
|
|
||
|
for (i=n=0;i<nurl;i++) {
|
||
|
n+=strstr(urls[i].path,"%s")||strstr(urls[i].path,"%S")?nsta:1;
|
||
|
}
|
||
|
nc=imat(n,1);
|
||
|
nt=imat(n,1);
|
||
|
for (i=0;i<n;i++) nc[i]=nt[i]=0;
|
||
|
|
||
|
for (;timediff(ts,te)<1E-3&&!abort;ts=timeadd(ts,ti*ncol)) {
|
||
|
|
||
|
genpath(datefmt==0?" %Y-":"%Y/%m/","",ts,0,year);
|
||
|
if (datefmt<=1) fprintf(fp,"%s %s",datefmt==0?"DOY ":"DATE",year);
|
||
|
else fprintf(fp,"WEEK ");
|
||
|
*date_p='\0'; flag=0;
|
||
|
|
||
|
m=datefmt==2?1:2;
|
||
|
|
||
|
for (i=0;i<(ncol+m-1)/m;i++) {
|
||
|
time=timeadd(ts,ti*i*m);
|
||
|
if (timediff(time,te)>=1E-3) break;
|
||
|
|
||
|
if (datefmt<=1) {
|
||
|
genpath(datefmt==0?"%n":"%d","",time,0,date);
|
||
|
fprintf(fp,"%-4s",strcmp(date,date_p)?date:"");
|
||
|
}
|
||
|
else {
|
||
|
if (fabs(time2gpst(time,&week))<1.0) {
|
||
|
fprintf(fp,"%04d",week); flag=1;
|
||
|
}
|
||
|
else {
|
||
|
fprintf(fp,"%s",flag?"":" "); flag=0;
|
||
|
}
|
||
|
}
|
||
|
strcpy(date_p,date);
|
||
|
}
|
||
|
fprintf(fp,"\n");
|
||
|
|
||
|
for (i=j=0;i<nurl&&!abort;i++) {
|
||
|
time=timeadd(ts,ti*ncol-1.0);
|
||
|
if (timediff(time,te)>=0.0) time=te;
|
||
|
|
||
|
/* test local files */
|
||
|
abort=test_locals(ts,time,ti,urls+i,stas,nsta,dir,nc+j,nt+j,fp);
|
||
|
|
||
|
j+=strstr(urls[i].path,"%s")||strstr(urls[i].path,"%S")?nsta:1;
|
||
|
}
|
||
|
fprintf(fp,"\n");
|
||
|
}
|
||
|
fprintf(fp,"# COUNT : FILES/TOTAL\n");
|
||
|
|
||
|
for (i=j=0;i<nurl;i++) {
|
||
|
j+=print_total(urls+i,stas,nsta,nc+j,nt+j,fp);
|
||
|
}
|
||
|
free(nc); free(nt);
|
||
|
}
|