📄 syncput.c
字号:
/* NcFTPSyncPut.c * * Utility to synchronize a remote site. This version only does incremental * updates by comparing recursive local directory listing to a previous listing. * This utility does not consider the actual contents of the remote directory, * so if you change files directly on the server this utility will not know * that those files have been changed. */#define VERSION "1.0.0"#ifdef HAVE_CONFIG_H# include <config.h>#endif#include <ncftp.h> /* Library header. */#include <Strn.h> /* Library header. */#if defined(WIN32) || defined(_WINDOWS)# include "..\ncftpget\gpshare.h"#else# include "../ncftpget/gpshare.h"#endif#ifdef HAVE_UTIME_H# include <utime.h>#else struct utimbuf { time_t actime, modtime; };#endifFTPLibraryInfo gLib;FTPConnectionInfo gConn;char gLDir[256];char gRDir[256];char gRDirActual[256];char gCatalogFile[256];char *gCatalogFileNameOnly; /* filename only */char gDebugCurCatalogFile[256];char gConfigFileNameOnly[80]; /* filename only */char gUmaskStr[16];int gDeleteRemoteFiles = 1;#ifdef WIN32int gRPathsToLowercase = 0;#endifFTPFileInfoList gFiles, gFilesToDelete, gPrevCatalog, gCurCatalog;char gTextExts[256] = ".txt;.html;.htm;.xml;.ini;.sh;.pl;.hqx;.cfg;.c;.h;.cpp;.hpp;.ps;.bat;.m3u;.pls";extern int gFirewallType;extern char gFirewallHost[64];extern char gFirewallUser[32];extern char gFirewallPass[32];extern unsigned int gFirewallPort;static voidPruneUnwantedFilesFromFileList(FTPFileInfoListPtr filp){ FTPFileInfoPtr fip, nextfip; char *cp; for (fip = filp->first; fip != NULL; fip = nextfip) { nextfip = fip->next; cp = StrRFindLocalPathDelim(fip->lname); if (cp == NULL) cp = fip->lname; else cp++; if ((fip->type != '-') && (fip->type != 'l')) nextfip = RemoveFileInfo(filp, fip); else if (strcasecmp(cp, gConfigFileNameOnly) == 0) nextfip = RemoveFileInfo(filp, fip); else if (strcasecmp(fip->lname, gCatalogFileNameOnly) == 0) nextfip = RemoveFileInfo(filp, fip);#ifdef WIN32 else if (gRPathsToLowercase != 0) { strlwr(fip->lname); strlwr(fip->relname); }#endif }} /* PruneUnwantedFilesFromFileList */static voidPrintFileList(FTPFileInfoListPtr filp){ FTPFileInfoPtr fip; struct tm *ltp, lt; int i, n; char mdtmstr[64]; n = filp->nFileInfos; for (i=0; i<n; i++) { fip = filp->vec[i]; ltp = Localtime(fip->mdtm, <); if (ltp == NULL) STRNCPY(mdtmstr, "(modtime unknown)"); else strftime(mdtmstr, sizeof(mdtmstr), "%Y-%m-%d %H:%M:%S", ltp); printf("%c %s size=%-10d %s\n", fip->type, mdtmstr, (int) fip->size, fip->lname ); }} /* PrintFileList */static intBreadthFirstCaseCmp(const void *a, const void *b){ char *cp, *cpa, *cpb; int depth, deptha, depthb; int c; const FTPFileInfo *const *fipa; const FTPFileInfo *const *fipb; fipa = (const FTPFileInfo *const *) a; fipb = (const FTPFileInfo *const *) b; cpa = (**fipa).relname; cpb = (**fipb).relname; for (cp = cpa, depth = 0;;) { c = *cp++; if (c == '\0') break; if ((c == '/') || (c == '\\')) { depth++; } } deptha = depth; for (cp = cpb, depth = 0;;) { c = *cp++; if (c == '\0') break; if ((c == '/') || (c == '\\')) { depth++; } } depthb = depth; if (deptha < depthb) return (-1); else if (deptha > depthb) return (1);#ifdef WIN32 /* Paths are generally not case-sensitive. */ return (stricmp(cpa, cpb));#elif defined(HAVE_SETLOCALE) return (strcoll(cpa, cpb));#else return (strcmp(cpa, cpb));#endif} /* BreadthFirstCaseCmp */static voidLoadCatalogFromFile(const char *const catalogFileName, FTPFileInfoListPtr const filp){ FILE *fp; char line[1024]; char *cp; char *tokstart; FTPFileInfo fi; int havesz, havemt; unsigned int mt; InitFileInfoList(filp); fp = fopen( catalogFileName,#ifdef WIN32 "rt"#else "r"#endif ); if (fp == NULL) { /* No previous catalog -- this is OK, * since this could be the first time * doing an update. */ return; } InitFileInfo(&fi); havesz = 0; havemt = 0; memset(line, 0, sizeof(line)); while (fgets(line, sizeof(line) - 1, fp) != NULL) { cp = line; while (*cp && isspace((int) *cp)) cp++; if ((*cp == '\0') || (cp[0] == '#') || (cp[0] == '[') || (cp[0] == ';') || (isspace((int) cp[0]))) continue; tokstart = cp; cp = line + strlen(line) - 1; if (*cp == '\n') { *cp-- = '\0'; if (*cp == '\r') *cp-- = '\0'; } if (strncasecmp(tokstart, "File: ", 6) == 0) { InitFileInfo(&fi); cp = tokstart + 6; fi.relnameLen = strlen(cp); fi.relname = StrDup(cp); fi.lname = StrDup(cp); fi.type = '-'; havesz = 0; havemt = 0; } else if (strncasecmp(tokstart, "Size: ", 6) == 0) { cp = tokstart + 6; (void) sscanf(cp, SCANF_LONG_LONG, (longest_int *) &fi.size); havesz++; } else if (strncasecmp(tokstart, "Date: ", 6) == 0) { cp = tokstart + 6; (void) sscanf(cp, "%u", (unsigned int *) &mt); fi.mdtm = (time_t) mt; havemt++; } if ((fi.relnameLen != 0) && (havesz != 0) && (havemt != 0)) { (void) AddFileInfo(filp, &fi); /* The FileInfo and its StrDup'ed contents * are now property of the list and will be * disposed of when the list is disposed of. */ /* Clear out the structure for re-use. */ InitFileInfo(&fi); } } (void) fclose(fp); PruneUnwantedFilesFromFileList(filp); VectorizeFileInfoList(filp); if (filp->nFileInfos > 1) { qsort(filp->vec, (size_t) filp->nFileInfos, sizeof(FTPFileInfoPtr), BreadthFirstCaseCmp); }} /* LoadCatalogFromFile */static voidLoadPreviousCatalogFile(void){ LoadCatalogFromFile(gCatalogFile, &gPrevCatalog);} /* LoadPreviousCatalogFile */static voidComputeCurrentCatalog(void){ FTPLineList directories; int result; InitFileInfoList(&gCurCatalog); InitLineList(&directories); AddLine(&directories, gLDir); result = FTPLocalRecursiveFileList2(&gConn, &directories, &gCurCatalog, 1); if (result < 0) { (void) fprintf(stderr, "NcFTPSyncPut: could not traverse %s: %s.\n", gLDir, FTPStrError(result)); DisposeWinsock(); DisposeLineListContents(&directories); exit(1); } DisposeLineListContents(&directories); PruneUnwantedFilesFromFileList(&gCurCatalog); VectorizeFileInfoList(&gCurCatalog); if (gCurCatalog.nFileInfos > 1) { qsort(gCurCatalog.vec, (size_t) gCurCatalog.nFileInfos, sizeof(FTPFileInfoPtr), BreadthFirstCaseCmp); }} /* ComputeCurrentCatalog */static intCopyFileInfoAndAddToFileList(FTPFileInfoPtr lp, FTPFileInfoListPtr filp){ FTPFileInfo newfi; newfi = *lp; newfi.relname = StrDup(lp->relname); newfi.lname = StrDup(lp->lname); newfi.rname = StrDup(lp->rname); newfi.rlinkto = StrDup(lp->rlinkto); newfi.plug = StrDup(lp->plug); if (AddFileInfo(filp, &newfi) == NULL) return (-1); return 0;} /* CopyFileInfoAndAddToFileList */static voidCollectListOfChangedFiles(void){ FTPFileInfoPtr prevfip, curfip; int iPrev, iCur; int cmprc; InitFileInfoList(&gFiles); InitFileInfoList(&gFilesToDelete); LoadPreviousCatalogFile(); if (gDebugCurCatalogFile[0] != '\0') LoadCatalogFromFile(gDebugCurCatalogFile, &gCurCatalog); /* for testing */ else ComputeCurrentCatalog(); /* We now have both the old catalog and current catalog * ready to compare. We next need to create a new list * containing just the list of files that we need to * update. */ iPrev = 0; iCur = 0; for (;;) { if (gCurCatalog.vec == NULL) break; /* done */ curfip = gCurCatalog.vec[iCur]; if (curfip == NULL) break; /* done */ for (;;) { if ( (gPrevCatalog.vec == NULL) || ((prevfip = gPrevCatalog.vec[iPrev]) == NULL) ) { /* Prev list did not have this file. */ (void) CopyFileInfoAndAddToFileList(curfip, &gFiles); break; } cmprc = BreadthFirstCaseCmp(&prevfip, &curfip); if (cmprc == 0) { /* Prev list _did_ have the file. * Now compare it by time/size. */ if ((prevfip->mdtm != curfip->mdtm) || (prevfip->size != curfip->size)) (void) CopyFileInfoAndAddToFileList(curfip, &gFiles); iPrev++; break; } else if (cmprc < 0) { /* Current list did not have file. * That means the file was deleted * since last run. */ (void) CopyFileInfoAndAddToFileList(prevfip, &gFilesToDelete); iPrev++; /* continue... */ } else /* if (cmprc > 0) */ { /* Previous list did not have this file. * That means we need to upload this file * which had been added since the last run. */ (void) CopyFileInfoAndAddToFileList(curfip, &gFiles); break; } } iCur++; } for (;;) { if (gPrevCatalog.vec == NULL) break; prevfip = gPrevCatalog.vec[iPrev]; if (prevfip == NULL) { break; } /* Current list did not have file. * That means the file was deleted * since last run. */ (void) CopyFileInfoAndAddToFileList(prevfip, &gFilesToDelete); iPrev++; } VectorizeFileInfoList(&gFiles); if (gFiles.nFileInfos > 1) { qsort(gFiles.vec, (size_t) gFiles.nFileInfos, sizeof(FTPFileInfoPtr), BreadthFirstCaseCmp); } VectorizeFileInfoList(&gFilesToDelete); if (gFilesToDelete.nFileInfos > 1) { qsort(gFilesToDelete.vec, (size_t) gFilesToDelete.nFileInfos, sizeof(FTPFileInfoPtr), BreadthFirstCaseCmp); }} /* CollectListOfChangedFiles */static voidUsage(void){ FILE *fp; fp = OpenPager(); (void) fprintf(fp, "NcFTPSyncPut %s.\n\n", VERSION); (void) fprintf(fp, "Usage:\n"); (void) fprintf(fp, " NcFTPSyncPut [flags] config.file\n"); (void) fprintf(fp, "\nFlags:\n\ -d XX Use the file XX for debug logging.\n\ -UU Update catalog file only, no uploading.\n\ -t XX Timeout after XX seconds.\n"); (void) fprintf(fp, "\ -v/-V Do (do not) use progress meters.\n\ -F Use passive (PASV) data connections.\n\ -y Try using \"SITE UTIME\" to preserve timestamps on remote host.\n"); (void) fprintf(fp, "\nExample minimal configuration file:\n\ [main]\n\ host=www.example.com\n\ user=gleason\n\ pass=mypassword\n\ ldir=/home/gleason/reports\n\ rdir=/users/g/gleason/public_html/report_dir\n\ catalog-file=/home/gleason/files/catalog.file\n"); (void) fprintf(fp, "\n\Other config file options:\n\ debug-log (same as \"-d\"; default is off)\n\ passive (yes/no, default: no)\n\ sync-deletes (yes/no, default: yes)\n\ port (default: 21)\n\ umask (default depends on server)\n\ textexts (default: %s)\n", gTextExts);#ifdef WIN32 (void) fprintf(fp, "\n\ pathnames-to-lowercase (default: no)\n");#endif (void) fprintf(fp, "\n\Note: If you choose to put the config file and/or the timestamp\n\ file in the directory to synchronize, NcFTPSyncPut will _not_\n\ upload those files _if_ they are detected.\n"); (void) fprintf(fp, "\nLibrary version: %s.\n", gLibNcFTPVersion + 5); (void) fprintf(fp, "\nThis is a freeware program by Mike Gleason (http://www.NcFTP.com/contact/).\n"); (void) fprintf(fp, "This was built using LibNcFTP (http://www.ncftp.com/libncftp).\n"); ClosePager(fp); DisposeWinsock(); exit(kExitUsage);} /* Usage */static voidAbort(int sigNum){ signal(sigNum, Abort); /* Hopefully the I/O operation in progress * will complete, and we'll abort before * it starts a new block. */ gConn.cancelXfer++; /* If the user appears to be getting impatient, * restore the default signal handler so the * next ^C abends the program. */ if (gConn.cancelXfer >= 2) signal(sigNum, SIG_DFL);} /* Abort */static intUpdateCatalogFile(void){ FILE *fp; FTPFileInfoPtr fip; int i, n; char mdtmstr[64]; fp = fopen( gCatalogFile,#ifdef WIN32 "wt"#else "w"#endif ); if (fp == NULL) { perror(gCatalogFile); return (-1); } n = gCurCatalog.nFileInfos; for (i=0; i<n; i++) { fip = gCurCatalog.vec[i]; (void) strftime(mdtmstr, sizeof(mdtmstr), "%Y-%m-%d %H:%M:%S", localtime(&fip->mdtm)); (void) fprintf(fp, "File: %s\nSize: ", fip->relname); (void) fprintf(fp, PRINTF_LONG_LONG, fip->size); (void) fprintf(fp, "\nDate: %lu (%s)\n\n", (unsigned long) fip->mdtm, mdtmstr); if (fflush(fp) < 0) { (void) fclose(fp); return (-1); } } return (0);} /* UpdateCatalogFile */static intGetFileTypeBasedOffOfExtension(const char *const fname){ char buf[256], *tok, extbuf[16]; const char *delims; const char *ext; int len; int i;
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -