/*************************************************************************** file : main.c project : cDirCmp (simple directory compare utility) begin : July 9, 2003 copyright : (C) 2003 by Paul Schuurmans email : paul.schuurmans@hccnet.nl ***************************************************************************/ /*************************************************************************** * * * This program is free software; you can redistribute it and/or modify * * it under the terms of the GNU General Public License as published by * * the Free Software Foundation; either version 2 of the License, or * * (at your option) any later version. * * * ***************************************************************************/ #include #include #include #include #include #include #include #include #include #include #define APPNAME "cDirCmp v0.3" #define MODE_OPTIONS 0 #define MODE_GETSRC 1 #define MODE_GETDST 2 #define MODE_REPORT 3 #define MODE_HELP 4 #define NUMHELPLINES 34 #define MAXFILES 10000 #define MAXPATH 260 #define MAXDIRS 1000 #define MAXLEVELS 16 #define BTN_GETSRC 0 #define TXT_SRC 1 #define BTN_GETDST 2 #define TXT_DST 3 #define CHK_SUBDIRS 4 #define CHK_INC_ALL 5 #define CHK_KEEP_ATTR 6 #define CHK_BACKUP 7 #define NUMFIELDS 8 // Format codes for StrCvt #define RWHITE 1 // Remove white space. #define RLWHITE 2 // Remove leading white space. #define RTWHITE 4 // Remove trailing white space. #define REDUCE 8 // Reduce white space to 1 blank. #define NOQUOTE 16 // Quoted substrings not altered. #define TOUP 32 // Convert to upper case. #define TOLOW 64 // Convert to lower case. #define RCONTROL 128 // Remove all control characters. #define TOCASE 256 // Toggle case #define FN_DIR 0x01 #define FN_FILE 0x02 #define FN_EXT 0x04 // Keycodes #define KEY_BS 8 #define KEY_TAB 9 #define KEY_S_TAB 353 #define KEY_RETURN 10 #define KEY_BKSPC 127 #define KEY_DEL 330 #define KEY_INS 331 #define KEY_PGDN 338 #define KEY_PGUP 339 #define KEY_ATERM_HOME 362 #define KEY_ETERM_HOME 299 #define KEY_XTERM_HOME 293 #define KEY_ATERM_END 385 #define KEY_ETERM_END 300 #define KEY_XTERM_END 296 #define KEY_ETERM_F1 342 #define KEY_ETERM_F2 343 #define KEY_ETERM_F3 344 #define KEY_ETERM_F4 345 void CheckDirNames (void); void ClearArray (void); int Copy (char *name); WINDOW *CreateWindow (int height, int width, int starty, int startx, int do_box); int FileExists (char *path); int Msg (char *txt, ...); int ReadDirectory (char *path); int ReadLine (FILE *rfile, char *line, int maxlinesize); int Report (void); int ReportDirDiffs (char *srcdir, int level); void SetCheckBox (void); void SetMode (int mode); void SetStatusBar (char *txt, ...); void SetTitleBar (char *txt, ...); void SetValidList (int set); void ShowInfo (int ndx); void SortList (void); void StartCopy (void); int StrChrIndex (char check, char *psearch); char *StrCvt (char *psource, int conv); void SwitchDirs (void); void UpdateDirList (int cmd); void UpdateFileList (int cmd); void UpdateHelp (int cmd); int WinGetCh (WINDOW *win); typedef struct { char Name[MAXPATH]; struct stat St; struct stat StD; bool Selected; int DiffCode; } TFileInfo; char CurDir[MAXPATH], SrcDir[MAXPATH], DstDir[MAXPATH]; int Mode, LastMode, SortMode, Quit; WINDOW *TitleBar, *OptionsWin, *MainWin, *StatusBar; FIELD *Fields[NUMFIELDS+1]; // 1 added for NULL at end FORM *Form; TFileInfo *Files[MAXFILES]; char *Dirs[MAXLEVELS][MAXDIRS]; int FwRows, FwCurItm, FwTopItm; int HelpRows, HelpTopLine, HelpLastTop; int FileCnt, SelCnt, DirCnt[MAXLEVELS], ValidList; char *ModeStrs[5] = { "H=Help Space=SetOption F6=SwitchSrcDst F7=Report F12=Exit", "Space=SetSource Enter=ChangeDir .=UseCurDir F12=Cancel", "Space=SetDestination Enter=ChangeDir .=UseCurDir F12=Cancel", "H=Help Space=Select A=All N=None S=Sort F8=Copy F12=Cancel", "Space=ExitHelp" }; char *HelpTxt[NUMHELPLINES] = { "cDirCmp is a simple utility that compares two directories, displays the ", "differences, and allows you to select items to copy. This is the ncurses ", "version of gDirCmp. This program basically uses two windows: the Options ", "window and this multi-purpose window that serves as either a Help, Directory, ", "or Report window.", "", "The Options Window", "------------------", "Start by selecting the source and destination directories, and any other ", "options you wish to set. Selections are made using the Space key.", "", "After setting the desired options, press the F7 key to show a report of the ", "differences found. Note that the report may take some time, depending on ", "how many files and/or directories need to be checked.", "", "The Report Window", "-----------------", "This window shows a list of differences between the source and destination ", "directories and allows you to select items to copy. Selections are made ", "using the Space key. The list may be sorted in one of two ways: by Filename ", "or by Difference Code. The 'S' key toggles between the two.", "", "The meanings of the Difference Codes are as follows:", " 1 File Not In Dst (file in Src does not exist in Dst)", " 2 Older In Dst (Src file is newer than Dst file)", " 3 Newer In Dst (Dst file is newer than Src file)", " 4 Smaller In Dst (Dst file is smaller than Src file)", " 5 Larger In Dst (Src file is smaller than Dst file)", " 6 Dir Not In Dst (directory in Src does not exist in Dst)", "", "After selecting the desired items in the Report window, you can use the F8 ", "key to copy the selected files and/or directories from Src to Dst. Or, you ", "can use the F12 key to return to the Options window. Note that returning ", "to the Options window invalidates the contents of the Report window." }; char *ConfigStrs[NUMFIELDS] = { "cb_srcdir", "txt_srcdir", "cb_dstdir", "txt_dstdir", "cb_subdirs", "cb_inc_all", "cb_keep_attr", "cb_backup" }; char *StStrs[7] = { " ", "File Not In Dst", "Older In Dst", "Newer In Dst", "Smaller In Dst", "Larger In Dst", "Dir Not In Dst" }; int main (int argc, char *argv[]) { FILE *fn = NULL; int w, h, ch, i, rows, cols, ndx; char configfile[MAXPATH], txt[1024] = "", line[1024] = ""; printf("%s (C)2003 JointProjects Software\n", APPNAME); getcwd(CurDir, MAXPATH); if(argc > 1) strcpy(SrcDir, argv[1]); if(argc > 2) strcpy(DstDir, argv[2]); CheckDirNames(); initscr(); cbreak(); noecho(); keypad(stdscr, TRUE); getmaxyx(stdscr, h, w); if((h < 24) || (w < 80)) { endwin(); printf("TermSize=%dx%d (need 80x24 min.)\n", w, h); return(1); } // Initialize Windows //////////////////////////////////////////////////// TitleBar = CreateWindow(1, w, 0, 0, 0); SetTitleBar("%s - Simple Directory Compare and File Copy Utility", APPNAME); OptionsWin = CreateWindow(6, w, 1, 0, 1); keypad(OptionsWin, TRUE); wrefresh(OptionsWin); MainWin = CreateWindow(h - 8, w, 7, 0, 1); keypad(MainWin, TRUE); StatusBar = CreateWindow(1, w, h - 1, 0, 0); FwRows = HelpRows = h - 10; HelpLastTop = NUMHELPLINES - HelpRows; mvwprintw(OptionsWin, 0, 1, " Options "); //mvwprintw(MainWin, 0, 1, " Help "); wrefresh(MainWin); /*+123456789+123456789+123456789+123456789+123456789+123456789+123456789+123456" "[+] Src: ____________________________________________________________________" "[+] Dst: ____________________________________________________________________" "[ ] Check All Subdirectories [ ] Include Hidden Files In Report" "[ ] Preserve File Attributes [ ] Backup Existing Files In Dst*/ Fields[0] = new_field(1, 1, 0, 1, 0, 0); Fields[1] = new_field(1, 68, 0, 9, 0, 0); Fields[2] = new_field(1, 1, 1, 1, 0, 0); Fields[3] = new_field(1, 68, 1, 9, 0, 0); Fields[4] = new_field(1, 1, 2, 1, 0, 0); Fields[5] = new_field(1, 1, 2, 40, 0, 0); Fields[6] = new_field(1, 1, 3, 1, 0, 0); Fields[7] = new_field(1, 1, 3, 40, 0, 0); Fields[8] = NULL; set_field_buffer(Fields[TXT_SRC], 0, " "); set_field_buffer(Fields[TXT_DST], 0, " "); field_opts_off(Fields[TXT_SRC], O_ACTIVE); field_opts_off(Fields[TXT_DST], O_ACTIVE); for(i = 0; i < NUMFIELDS; i++) { field_opts_off(Fields[i], O_AUTOSKIP); set_field_fore(Fields[i], A_BOLD); set_field_pad(Fields[i], 0); } Form = new_form(Fields); scale_form(Form, &rows, &cols); set_form_win(Form, OptionsWin); set_form_sub(Form, derwin(OptionsWin, rows, cols, 1, 2)); post_form(Form); ShowInfo(-1); // Read configuration file /////////////////////////////////////////////// sprintf(configfile, "%s/.cdircmp.cfg", getenv("HOME")); if((fn = fopen(configfile, "rb")) != NULL) { for(i = 0; i < NUMFIELDS; i++) { txt[0] = 0; if((ReadLine(fn, line, 1000)) != -1 && (ndx = StrChrIndex('=', line)) != -1) { strcpy(txt, line + ndx + 1); if(i == TXT_SRC) strcpy(SrcDir, txt); else if(i == TXT_DST) strcpy(DstDir, txt); } set_field_buffer(Fields[i], 0, txt); } fclose(fn); CheckDirNames(); } else { set_field_buffer(Fields[BTN_GETSRC], 0, "+"); set_field_buffer(Fields[TXT_SRC], 0, SrcDir); set_field_buffer(Fields[BTN_GETDST], 0, "+"); set_field_buffer(Fields[TXT_DST], 0, DstDir); } set_current_field(Form, Fields[0]); //UpdateHelp(0); wrefresh(OptionsWin); LastMode = MODE_OPTIONS; SetMode(MODE_OPTIONS); // Main loop ///////////////////////////////////////////////////////////// while(!Quit) { if(Mode) ch = WinGetCh(MainWin); else ch = WinGetCh(OptionsWin); if(Mode == MODE_OPTIONS) { switch(ch) { case KEY_DOWN: case KEY_RIGHT: case 9: form_driver(Form, REQ_NEXT_FIELD); break; case KEY_UP: case KEY_LEFT: case 353: form_driver(Form, REQ_PREV_FIELD); break; case ' ': case KEY_RETURN: SetCheckBox(); break; case KEY_F(6): SwitchDirs(); break; case KEY_F(7): if(Report()) SetMode(MODE_REPORT); break; case 'h': case 'H': LastMode = MODE_OPTIONS; SetMode(MODE_HELP); break; case KEY_F(12): SetStatusBar(""); Quit++; break; default: break; } } else if(Mode == MODE_GETSRC || Mode == MODE_GETDST) { switch(ch) { case KEY_DOWN: case KEY_UP: case KEY_PGDN: case KEY_PGUP: case KEY_HOME: case KEY_ATERM_HOME: case KEY_ETERM_HOME: case KEY_XTERM_HOME: case KEY_END: case KEY_ATERM_END: case KEY_ETERM_END: case KEY_XTERM_END: case KEY_RETURN: case ' ': case '.': case KEY_F(12): UpdateDirList(ch); break; default: break; } } else if(Mode == MODE_REPORT) { switch(ch) { case KEY_DOWN: case KEY_UP: case KEY_PGDN: case KEY_PGUP: case KEY_HOME: case KEY_END: case KEY_ATERM_HOME: case KEY_ETERM_HOME: case KEY_XTERM_HOME: case KEY_ATERM_END: case KEY_ETERM_END: case KEY_XTERM_END: UpdateFileList(ch); break; case KEY_RETURN: case ' ': UpdateFileList(ch); break; case 'a': case 'A': for(i = 0; i < FileCnt; i++) Files[i]->Selected = 1; UpdateFileList(0); break; case 'n': case 'N': for(i = 0; i < FileCnt; i++) Files[i]->Selected = 0; UpdateFileList(0); break; case 'h': case 'H': LastMode = MODE_REPORT; SetMode(MODE_HELP); break; case 's': case 'S': SortMode = !SortMode; SortList(); UpdateFileList(0); break; case KEY_F(8): StartCopy(); break; case KEY_F(12): SetValidList(0); ShowInfo(-1); SetMode(MODE_OPTIONS); break; default: break; } } else if(Mode == MODE_HELP) { switch(ch) { case KEY_DOWN: case KEY_UP: case KEY_PGDN: case KEY_PGUP: UpdateHelp(ch); break; case ' ': SetMode(LastMode); break; default: break; } } } // Save settings, cleanup, and exit ////////////////////////////////////// if((fn = fopen(configfile, "w+")) != NULL) { for(i = 0; i < NUMFIELDS; i++) { if(i == TXT_SRC) strcpy(txt, SrcDir); else if(i == TXT_DST) strcpy(txt, DstDir); else strcpy(txt, field_buffer(Fields[i], 0)); fprintf(fn, "%s=%s\n", ConfigStrs[i], StrCvt(txt, RTWHITE)); } fclose(fn); } ClearArray(); unpost_form(Form); for(i = 0; i < NUMFIELDS; i++) free_field(Fields[i]); delwin(TitleBar); delwin(MainWin); delwin(OptionsWin); delwin(StatusBar); endwin(); return(0); } void CheckDirNames () { struct stat st; int res; res = stat(SrcDir, &st); if(res < 0 || !S_ISDIR(st.st_mode)) strcpy(SrcDir, CurDir); res = stat(DstDir, &st); if(res < 0 || !S_ISDIR(st.st_mode)) strcpy(DstDir, CurDir); set_field_buffer(Fields[TXT_SRC], 0, SrcDir); set_field_buffer(Fields[TXT_DST], 0, DstDir); } void ClearArray () { int i, j; FwCurItm = FwTopItm = 0; for(i = 0; i < FileCnt; i++) { free(Files[i]); Files[i] = NULL; } FileCnt = 0; for(i = 0; i < MAXLEVELS; i++) { for(j = 0; j < DirCnt[i]; j++) { free(Dirs[i][j]); Dirs[i][j] = NULL; } DirCnt[i] = 0; } } int Copy (char *name) { char cmd[2048] = "", args[16] = ""; if(*(field_buffer(Fields[CHK_KEEP_ATTR], 0)) == '*') strcpy(args, "-a"); else strcpy(args, "-dR"); if(*(field_buffer(Fields[CHK_BACKUP], 0)) == '*') strcat(args, "b"); SetStatusBar("Copying %s...", name); sprintf(cmd, "cp %s \"%s/%s\" \"%s/%s\"", args, SrcDir, name, DstDir, name); if(system(cmd) == -1) return(0); return(1); } WINDOW *CreateWindow (int height, int width, int starty, int startx, int do_box) { WINDOW *win; win = newwin(height, width, starty, startx); if(do_box) box(win, 0, 0); wrefresh(win); return(win); } int FileExists (char *path) { return(access(path, 0) == 0); } int Msg (char *txt, ...) { char msg[1024] = ""; int ch; va_list args; va_start(args, txt); vsprintf(msg, txt, args); va_end(args); wclear(StatusBar); mvwprintw(StatusBar, 0, 1, msg); wrefresh(StatusBar); ch = wgetch(StatusBar); SetMode(Mode); if(ch == 'y' || ch == 'Y') return(1); return(0); } int ReadDirectory (char *dname) { DIR *dp; struct dirent *entry; struct stat st; TFileInfo *fi; char dirname[1024], filename[1024]; int cnt = 0; strcpy(dirname, dname); if((dp = opendir(dirname)) == NULL) { SetStatusBar("Cannot Open Directory: %s", dirname); getcwd(dirname, MAXPATH); dp = opendir(dirname); } FwCurItm = FwTopItm = 0; SetStatusBar("Checking %s...", dirname); ClearArray(); while(((entry = readdir(dp)) != NULL) && (FileCnt < MAXFILES)) { sprintf(filename, "%s/%s", dirname, entry->d_name); if(stat(filename, &st) != 0) continue; if(S_ISDIR(st.st_mode) && strcmp(".", entry->d_name)) { if((fi = calloc(1, sizeof(TFileInfo)))) { strcpy(fi->Name, entry->d_name); stat(filename, &fi->St); Files[FileCnt++] = fi; cnt++; } else { SetStatusBar("Allocation Error! Files[%d]", FileCnt); } } } closedir(dp); SortList(); strcpy(CurDir, dirname); UpdateDirList(0); return(cnt); } int ReadLine (FILE *rfile, char *line, int maxlinesize) { register int ch, c = 0; do { while((ch = getc(rfile)) == 13); if((ch == 26) || (ch == -1)) return(-1); if(ch == 10) break; line[c] = ch; c++; } while(c < maxlinesize); line[c] = 0; return(c); } int Report () { int chksubs = 0, i, j; if(!SrcDir[0] || !DstDir[0]) { Msg("Src and/or Dst not yet set"); SetMode(Mode); return(0); } if(strcmp(SrcDir, DstDir) == 0) { Msg("Src and Dst are the same!"); return(0); } ClearArray(); ReportDirDiffs(0, 0); if(*(field_buffer(Fields[CHK_SUBDIRS], 0)) == '*') chksubs = 1; for(i = 1; chksubs && i < MAXLEVELS && DirCnt[i]; i++) { for(j = 0; j < DirCnt[i]; j++) ReportDirDiffs(Dirs[i][j], i); } if(FileCnt) { ValidList++; SetMode(MODE_REPORT); FwCurItm = FwTopItm = 0; SortList(); ShowInfo(0); UpdateFileList(0); } else { Msg("No differences found."); SetMode(MODE_OPTIONS); } return(FileCnt); } int ReportDirDiffs (char *srcdir, int level) { DIR *dp; struct dirent *entry; struct stat srcbuf, dstbuf; //struct tm *sts, *dts; char srcname[MAXPATH], dstname[MAXPATH], srcdirname[1024], dstdirname[1024]; char temp[1024]; time_t srctime, dsttime; off_t srcsize = 0, dstsize = 0; int addit = 0, showHidden = 0; if(level) { sprintf(srcdirname, "%s/%s", SrcDir, srcdir); sprintf(dstdirname, "%s/%s", DstDir, srcdir); } else { strcpy(srcdirname, SrcDir); strcpy(dstdirname, DstDir); } if((dp = opendir(srcdirname)) == NULL) { SetStatusBar("opendir failed: %s", srcdirname); return(0); } SetStatusBar("Checking %s...", srcdirname); if(*(field_buffer(Fields[CHK_INC_ALL], 0)) == '*') showHidden = 1; while((entry = readdir(dp)) != NULL) { addit = 0; if(entry->d_name[0] == '.' && !showHidden) continue; sprintf(srcname, "%s/%s", srcdirname, entry->d_name); sprintf(dstname, "%s/%s", dstdirname, entry->d_name); if(stat(srcname, &srcbuf) != 0) continue; srcsize = srcbuf.st_size; srctime = srcbuf.st_mtime; if(FileExists(dstname)) { if(stat(dstname, &dstbuf) != 0) continue; if(S_ISREG(dstbuf.st_mode)) { dstsize = dstbuf.st_size; dsttime = dstbuf.st_mtime; if(srcsize != dstsize) addit = (dstsize < srcsize) ? 4 : 5; if(srctime != dsttime) addit = (dsttime < srctime) ? 2 : 3; } else if(S_ISDIR(srcbuf.st_mode) && S_ISDIR(dstbuf.st_mode)) { if(!strcmp(".", entry->d_name) || !strcmp("..", entry->d_name)) continue; if(level) sprintf(temp, "%s/%s", srcdir, entry->d_name); else strcpy(temp, entry->d_name); if((level + 1) < (MAXLEVELS - 1)) { if((Dirs[level+1][DirCnt[level+1]] = calloc(1, strlen(temp) + 1))) { strcpy(Dirs[level+1][DirCnt[level+1]], temp); DirCnt[level+1]++; } else { SetStatusBar("Allocation Error! Dirs[%d][%d]", level+1, DirCnt[level+1]); return(0); } } else { SetStatusBar("Maximum Levels Reached!"); return(0); } } } else { if(S_ISREG(srcbuf.st_mode)) addit = 1; else if(S_ISDIR(srcbuf.st_mode) && strcmp(".", entry->d_name) && strcmp("..", entry->d_name)) addit = 6; } if(addit) { if((Files[FileCnt] = calloc(1, sizeof(TFileInfo)))) { stat(srcname, &Files[FileCnt]->St); if(level) sprintf(Files[FileCnt]->Name, "%s/%s", srcdir, entry->d_name); else strcpy(Files[FileCnt]->Name, entry->d_name); //sts = localtime(&srctime); //sprintf(Files[FileCnt]->Sts, "%04d.%02d.%02d %02d.%02d %10ld", sts->tm_year+1900, sts->tm_mon+1, sts->tm_mday, sts->tm_hour, sts->tm_min, srcsize); if((addit > 1) && (addit < 6)) { stat(dstname, &Files[FileCnt]->StD); //dts = localtime(&dsttime); //sprintf(Files[FileCnt]->Dts, "%04d.%02d.%02d %02d.%02d %10ld", dts->tm_year+1900, dts->tm_mon+1, dts->tm_mday, dts->tm_hour, dts->tm_min, dstsize); } Files[FileCnt]->DiffCode = addit; FileCnt++; } else { SetStatusBar("Allocation Error! Files[%d]", FileCnt); return(0); } } } closedir(dp); return(1); } void SetCheckBox () { int ndx; char *fldbuf; ndx = field_index(current_field(Form)); switch(ndx) { case BTN_GETSRC: SetMode(MODE_GETSRC); //strcpy(dirname, field_buffer(Fields[TXT_SRC], 0)); //FileCnt = ReadDirectory(StrCvt(dirname, RTWHITE)); FileCnt = ReadDirectory(SrcDir); break; case BTN_GETDST: SetMode(MODE_GETDST); //strcpy(dirname, field_buffer(Fields[TXT_DST], 0)); //FileCnt = ReadDirectory(StrCvt(dirname, RTWHITE)); FileCnt = ReadDirectory(DstDir); break; case CHK_SUBDIRS: case CHK_INC_ALL: case CHK_KEEP_ATTR: case CHK_BACKUP: fldbuf = field_buffer(Fields[ndx], 0); if(*fldbuf == ' ') form_driver(Form, '*'); else form_driver(Form, ' '); form_driver(Form, REQ_NEXT_FIELD); form_driver(Form, REQ_PREV_FIELD); set_current_field(Form, Fields[ndx]); break; default: break; } } void SetMode (int mode) { Mode = mode; SetStatusBar(ModeStrs[Mode]); if(mode == MODE_OPTIONS) { ShowInfo(-1); wrefresh(OptionsWin); form_driver(Form, REQ_NEXT_FIELD); form_driver(Form, REQ_PREV_FIELD); } else { if(mode == MODE_HELP) UpdateHelp(0); else if(mode == MODE_REPORT) UpdateFileList(0); else wmove(MainWin, (FwCurItm - FwTopItm) + 1, 1); wrefresh(MainWin); } } void SetStatusBar (char *txt, ...) { char msg[1024] = ""; va_list args; va_start(args, txt); vsprintf(msg, txt, args); va_end(args); wclear(StatusBar); mvwprintw(StatusBar, 0, 1, msg); wrefresh(StatusBar); } void SetTitleBar (char *txt, ...) { char msg[1024] = ""; va_list args; va_start(args, txt); vsprintf(msg, txt, args); va_end(args); wclear(TitleBar); mvwprintw(TitleBar, 0, 1, msg); wrefresh(TitleBar); } void SetValidList (int set) { if(set) ValidList++; else { ValidList = 0; if(FileCnt) { wclear(MainWin); box(MainWin, 0, 0); wrefresh(MainWin); } } } void ShowInfo (int ndx) { int i; struct tm *ts; char txt[80], temp[40] = ""; char *strs[4] = { "[+] Src:", "[+] Dst:", "[ ] Check All Subdirectories [ ] Include Hidden Files In Report", "[ ] Preserve File Attributes [ ] Make Backups Of Existing Files" }; wclear(OptionsWin); box(OptionsWin, 0, 0); if(ndx < 0) { mvwprintw(OptionsWin, 0, 1, " Options "); for(i = 0; i < 4; i++) mvwprintw(OptionsWin, i + 1, 2, "%s", strs[i]); for(i = 0; i < NUMFIELDS; i++) set_field_buffer(Fields[i], 0, field_buffer(Fields[i], 0)); } else { mvwprintw(OptionsWin, 0, 2, " Information "); mvwprintw(OptionsWin, 1, 2, Files[ndx]->Name); mvwprintw(OptionsWin, 2, 2, "%s", StStrs[Files[ndx]->DiffCode]); temp[0] = (Files[ndx]->St.st_mode & S_IRUSR) ? 'r' : '-'; temp[1] = (Files[ndx]->St.st_mode & S_IWUSR) ? 'w' : '-'; temp[2] = (Files[ndx]->St.st_mode & S_IXUSR) ? 'x' : '-'; temp[3] = (Files[ndx]->St.st_mode & S_IRGRP) ? 'r' : '-'; temp[4] = (Files[ndx]->St.st_mode & S_IWGRP) ? 'w' : '-'; temp[5] = (Files[ndx]->St.st_mode & S_IXGRP) ? 'x' : '-'; temp[6] = (Files[ndx]->St.st_mode & S_IROTH) ? 'r' : '-'; temp[7] = (Files[ndx]->St.st_mode & S_IWOTH) ? 'w' : '-'; temp[8] = (Files[ndx]->St.st_mode & S_IXOTH) ? 'x' : '-'; temp[9] = 0; ts = localtime(&Files[ndx]->St.st_mtime); sprintf(txt, "%10ld %04d.%02d.%02d %02d.%02d %s %4d %4d", Files[ndx]->St.st_size, ts->tm_year+1900, ts->tm_mon+1, ts->tm_mday, ts->tm_hour, ts->tm_min, temp, Files[ndx]->St.st_uid, Files[ndx]->St.st_gid); mvwprintw(OptionsWin, 3, 2, "Src: %s", txt); if((Files[ndx]->DiffCode > 1) && (Files[ndx]->DiffCode < 6)) { temp[0] = (Files[ndx]->StD.st_mode & S_IRUSR) ? 'r' : '-'; temp[1] = (Files[ndx]->StD.st_mode & S_IWUSR) ? 'w' : '-'; temp[2] = (Files[ndx]->StD.st_mode & S_IXUSR) ? 'x' : '-'; temp[3] = (Files[ndx]->StD.st_mode & S_IRGRP) ? 'r' : '-'; temp[4] = (Files[ndx]->StD.st_mode & S_IWGRP) ? 'w' : '-'; temp[5] = (Files[ndx]->StD.st_mode & S_IXGRP) ? 'x' : '-'; temp[6] = (Files[ndx]->StD.st_mode & S_IROTH) ? 'r' : '-'; temp[7] = (Files[ndx]->StD.st_mode & S_IWOTH) ? 'w' : '-'; temp[8] = (Files[ndx]->StD.st_mode & S_IXOTH) ? 'x' : '-'; temp[9] = 0; ts = localtime(&Files[ndx]->StD.st_mtime); sprintf(txt, "%10ld %04d.%02d.%02d %02d.%02d %s %4d %4d", Files[ndx]->StD.st_size, ts->tm_year+1900, ts->tm_mon+1, ts->tm_mday, ts->tm_hour, ts->tm_min, temp, Files[ndx]->StD.st_uid, Files[ndx]->StD.st_gid); } else txt[0] = 0; mvwprintw(OptionsWin, 4, 2, "Dst: %s", txt); } wrefresh(OptionsWin); } void SortList () { int i, j; char astr[1024], bstr[1024]; TFileInfo *tmp; for(i = 0; i < FileCnt - 1; i++) { for(j = i + 1; j < FileCnt; j++) { if(Mode == MODE_REPORT && SortMode) { sprintf(astr, "%d%d%s", Files[i]->DiffCode, (S_ISDIR(Files[i]->St.st_mode)) ? 0 : 1, Files[i]->Name); sprintf(bstr, "%d%d%s", Files[j]->DiffCode, (S_ISDIR(Files[j]->St.st_mode)) ? 0 : 1, Files[j]->Name); } else { sprintf(astr, "%d%s", (S_ISDIR(Files[i]->St.st_mode)) ? 0 : 1, Files[i]->Name); sprintf(bstr, "%d%s", (S_ISDIR(Files[j]->St.st_mode)) ? 0 : 1, Files[j]->Name); } if(strcmp(astr, bstr) > 0) { tmp = Files[i]; Files[i] = Files[j]; Files[j] = tmp; } } } } void StartCopy () { char fname[MAXPATH]; int i, selcnt = 0; for(i = 0; i < FileCnt; i++) if(Files[i]->Selected) selcnt++; if(!selcnt) { Msg("Nothing selected."); return; } ShowInfo(-1); if(!Msg("Copy %d item%sfrom Src to Dst (y/N)? ", selcnt, (selcnt == 1) ? " " : "s ")) return; for(i = 0; i < FileCnt; i++) { if(!Files[i]->Selected) continue; strcpy(fname, Files[i]->Name); Copy(fname); } SetValidList(0); SetMode(MODE_OPTIONS); } int StrChrIndex (char check, char *psearch) { char *p; if((p = strchr(psearch, check)) == NULL) return (-1); return((int) (p - psearch)); } char *StrCvt (char *psource, int conv) { char *pfrom = psource; //Next character to get fron source char *pto = psource; // Next position to fill in target char c; char quote_char = '\0'; int rlwhite = conv & RLWHITE; int rwhite = conv & RWHITE; int reduce = (!rwhite) && (conv & REDUCE); int ckquotes = conv & NOQUOTE; int to_up = conv & TOUP; int to_low = conv & TOLOW; int rcontrol = conv & RCONTROL; int to_case = conv & TOCASE; int in_white = FALSE; // Not in a white field yet. int hit_nonwhite = FALSE; // No nonwhite chars encountered. int quote_on = FALSE; // Not in a quote yet. while((c = *pfrom++) != '\0') { if(quote_on) { *pto++ = c; if(c == quote_char) quote_on = FALSE; } else if (ckquotes && ((c == '"') || (c == '\''))) { *pto++ = c; in_white = FALSE; hit_nonwhite = TRUE; quote_on = TRUE; quote_char = c; } else if(isspace(c) && isascii(c)) { if(rwhite) ; else if(rlwhite && !hit_nonwhite) ; else if(reduce) { if(in_white) ; else { *pto++ = ' '; in_white = TRUE; } } else if(rcontrol && iscntrl(c)) ; else *pto++ = c; } else if(iscntrl(c) && isascii(c)) { in_white = FALSE; hit_nonwhite = TRUE; if (rcontrol) ; else *pto++ = c; } else { in_white = FALSE; hit_nonwhite = TRUE; if(isascii(c)) { if(to_up) c = toupper(c); if(to_low) c = tolower(c); if(to_case) c = islower(c) ? toupper(c) : tolower(c); } *pto++ = c; } } *pto = '\0'; if(conv & RTWHITE) { for(c = *--pto; isspace(c) && isascii(c) && (pto >= psource); c = *--pto) *pto = '\0'; } return(psource); } void SwitchDirs () { char txt[1024]; strcpy(txt, SrcDir); strcpy(SrcDir, DstDir); strcpy(DstDir, txt); set_field_buffer(Fields[TXT_SRC], 0, SrcDir); set_field_buffer(Fields[TXT_DST], 0, DstDir); ValidList = 0; } void UpdateDirList (int cmd) { int i, len, update = 0, end = 0; char txt[1024]; TFileInfo *fi; switch(cmd) { case KEY_PGDN: for(i = 0; i < FwRows && FwCurItm < FileCnt - 1; i++) { if(FwCurItm < FileCnt - 1) FwCurItm++; if((FwCurItm - FwTopItm + 1) > FwRows) { FwTopItm++; update++; } } break; case KEY_PGUP: for(i = FwRows; i && FwCurItm; i--) { if(FwCurItm) FwCurItm--; if(FwCurItm < FwTopItm) { FwTopItm--; update++; } } break; case KEY_DOWN: if(FwCurItm < FileCnt - 1) FwCurItm++; if((FwCurItm - FwTopItm + 1) > FwRows) { FwTopItm++; update++; } else wmove(MainWin, (FwCurItm - FwTopItm) + 1, 1); break; case KEY_UP: if(FwCurItm) FwCurItm--; if(FwCurItm < FwTopItm) { FwTopItm--; update++; } else wmove(MainWin, (FwCurItm - FwTopItm) + 1, 1); break; case KEY_HOME: case KEY_ATERM_HOME: case KEY_ETERM_HOME: case KEY_XTERM_HOME: FwCurItm = FwTopItm = 0; update++; break; case KEY_END: case KEY_ATERM_END: case KEY_ETERM_END: case KEY_XTERM_END: FwCurItm = FileCnt - 1; if((FwCurItm - FwTopItm + 1) > FwRows) { FwTopItm = (FwCurItm - FwRows) + 1; update++; } else wmove(MainWin, (FwCurItm - FwTopItm) + 1, 1); break; case ' ': fi = Files[FwCurItm]; if(strcmp(fi->Name, "..") != 0) { if(strlen(CurDir) == 1 && CurDir[0] == '/') CurDir[0] = 0; if(Mode == MODE_GETSRC) { sprintf(SrcDir, "%s/%s", CurDir, fi->Name); set_field_buffer(Fields[TXT_SRC], 0, SrcDir); } else if(Mode == MODE_GETDST) { sprintf(DstDir, "%s/%s", CurDir, fi->Name); set_field_buffer(Fields[TXT_DST], 0, DstDir); } end++; } break; case KEY_RETURN: fi = Files[FwCurItm]; if(!strcmp(fi->Name, "..")) { strcpy(txt, CurDir); len = strlen(txt); if(len) { for(i = len - 1; i && (txt[i] != '/'); i--) txt[i] = 0; txt[i] = 0; } } else { if(strlen(CurDir) == 1 && CurDir[0] == '/') sprintf(txt, "/%s", Files[FwCurItm]->Name); else sprintf(txt, "%s/%s", CurDir, Files[FwCurItm]->Name); } if(!strlen(txt)) strcpy(txt, "/"); FileCnt = ReadDirectory(txt); update++; break; case '.': if(Mode == MODE_GETSRC) { strcpy(SrcDir, CurDir); set_field_buffer(Fields[TXT_SRC], 0, SrcDir); } else if(Mode == MODE_GETDST) { strcpy(DstDir, CurDir); set_field_buffer(Fields[TXT_DST], 0, DstDir); } end++; break; case KEY_F(12): end++; break; default: break; } if(end) { wclear(MainWin); box(MainWin, 0, 0); wrefresh(MainWin); SetMode(MODE_OPTIONS); } if(update || !cmd) { SetStatusBar(ModeStrs[Mode]); wclear(MainWin); box(MainWin, 0, 0); mvwprintw(MainWin, 0, 1, " %s ", CurDir); for(i = 0; i < FwRows && i < FileCnt; i++) { fi = Files[i+FwTopItm]; strcpy(txt, (fi->Selected) ? ">" : " "); strcat(txt, fi->Name); mvwprintw(MainWin, i+1, 1, txt); } wmove(MainWin, (FwCurItm - FwTopItm) + 1, 1); wrefresh(MainWin); } } void UpdateFileList (int cmd) { int i, update = 0, selchanged = 0; char txt[1024]; TFileInfo *fi; switch(cmd) { case KEY_DOWN: if(FwCurItm < FileCnt - 1) { FwCurItm++; if((FwCurItm - FwTopItm + 1) > FwRows) { FwTopItm++; update++; } else wmove(MainWin, (FwCurItm - FwTopItm) + 1, 1); selchanged++; } break; case KEY_PGDN: if(FwCurItm < FileCnt - 1) { for(i = 0; i < FwRows; i++) { if(FwCurItm < FileCnt - 1) FwCurItm++; if((FwCurItm - FwTopItm + 1) > FwRows) { FwTopItm++; update++; } else wmove(MainWin, (FwCurItm - FwTopItm) + 1, 1); } selchanged++; } break; case KEY_UP: if(FwCurItm) { FwCurItm--; if(FwCurItm < FwTopItm) { FwTopItm--; update++; } else wmove(MainWin, (FwCurItm - FwTopItm) + 1, 1); selchanged++; } break; case KEY_PGUP: if(FwCurItm) { for(i = FwRows; i; i--) { if(FwCurItm) FwCurItm--; if(FwCurItm < FwTopItm) { FwTopItm--; update++; } else wmove(MainWin, (FwCurItm - FwTopItm) + 1, 1); } selchanged++; } break; case KEY_HOME: case KEY_ATERM_HOME: case KEY_ETERM_HOME: case KEY_XTERM_HOME: FwCurItm = FwTopItm = 0; update++; selchanged++; break; case KEY_END: case KEY_ATERM_END: case KEY_ETERM_END: case KEY_XTERM_END: FwCurItm = FileCnt - 1; if((FwCurItm - FwTopItm + 1) > FwRows) { FwTopItm = (FwCurItm - FwRows) + 1; update++; } else wmove(MainWin, (FwCurItm - FwTopItm) + 1, 1); selchanged++; break; case ' ': fi = Files[FwCurItm]; if(fi->Selected) { fi->Selected = 0; SelCnt--; } else { fi->Selected = 1; SelCnt++; } if(S_ISDIR(fi->St.st_mode)) sprintf(txt, "%c %d {%s}", (fi->Selected) ? '>' : ' ', fi->DiffCode, fi->Name); else sprintf(txt, "%c %d %s", (fi->Selected) ? '>' : ' ', fi->DiffCode, fi->Name); mvwprintw(MainWin, (FwCurItm - FwTopItm) + 1, 1, txt); wmove(MainWin, (FwCurItm - FwTopItm) + 1, 1); break; default: break; } if(selchanged || !cmd) ShowInfo(FwCurItm); if(update || !cmd) { wclear(MainWin); box(MainWin, 0, 0); mvwprintw(MainWin, 0, 1, " %d New Or Different ", FileCnt); for(i = 0; i < FwRows && i < FileCnt; i++) { fi = Files[i+FwTopItm]; if(S_ISDIR(fi->St.st_mode)) sprintf(txt, "%c %d {%s}", (fi->Selected) ? '>' : ' ', fi->DiffCode, fi->Name); else sprintf(txt, "%c %d %s", (fi->Selected) ? '>' : ' ', fi->DiffCode, fi->Name); mvwprintw(MainWin, i+1, 1, txt); } wmove(MainWin, (FwCurItm - FwTopItm) + 1, 1); wrefresh(MainWin); } } void UpdateHelp (int cmd) { int i, update = 0; switch(cmd) { case KEY_PGDN: if(HelpTopLine != HelpLastTop) { for(i = 0; i < HelpRows && HelpTopLine < HelpLastTop; i++) HelpTopLine++; update++; } break; case KEY_PGUP: if(HelpTopLine) { for(i = HelpRows; i && HelpTopLine; i--) HelpTopLine--; update++; } break; case KEY_DOWN: if(HelpTopLine < HelpLastTop) { HelpTopLine++; update++; } break; case KEY_UP: if(HelpTopLine) { HelpTopLine--; update++; } break; case KEY_HOME: case KEY_ATERM_HOME: case KEY_ETERM_HOME: case KEY_XTERM_HOME: if(HelpTopLine) { HelpTopLine = 0; update++; } break; case KEY_END: case KEY_ATERM_END: case KEY_ETERM_END: case KEY_XTERM_END: if(HelpTopLine != HelpLastTop) { HelpTopLine = HelpLastTop; update++; } break; default: break; } if(update || !cmd) { wclear(MainWin); box(MainWin, 0, 0); mvwprintw(MainWin, 0, 1, " Help "); for(i = 0; i < HelpRows; i++) mvwprintw(MainWin, i+1, 1, HelpTxt[HelpTopLine + i]); wmove(MainWin, 1, 1); wrefresh(MainWin); } } int WinGetCh (WINDOW *win) { int ch, ndx = 0, wch[5] = { 0, 0, 0, 0, 0 }, done = 0; while(!done) { ch = wgetch(win); wch[ndx] = ch; switch(ndx) { case 0: if(ch == 27) ndx++; else done++; break; case 1: if(ch == 91 || ch == 79) ndx++; else done++; break; case 2: if(wch[1] == 91 || wch[1] == 79) { if(wch[1] == 79) { ch = wch[0] + wch[1] + wch[2] + 156; done++; } else ndx++; } else { ch = wch[0] + wch[1] + wch[2]; done++; } break; case 3: if(wch[1] == 91 && ch != 126) ndx++; else { ch = wch[0] + wch[1] + wch[2] + wch[3]; done++; } break; case 4: ch = wch[0] + wch[1] + wch[2] + wch[3] + wch[4]; done++; break; } } return(ch); }