/* Panels management file Copyright (c) Tudor Hulubei & Andrei Pitis, May 1994 This file is part of UIT (UNIX Interactive Tools) UIT 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, or (at your option) any later version. UIT is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details . You should have received a copy of the GNU General Public License along with UIT; see the file COPYING. If not, write to the Free Software Foundation, 675 Mass Ave, Cambridge, MA 02139, USA. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #ifdef HAVE_LINUX #include #endif #include "tty.h" #include "window.h" #include "status.h" #include "panel.h" /* This is really ugly ... */ #ifndef S_IFMT #define S_IFMT 00170000 #endif #ifndef S_IFSOCK #define S_IFSOCK 0140000 #endif #ifndef S_IFLNK #define S_IFLNK 0120000 #endif #ifndef S_IFREG #define S_IFREG 0100000 #endif #ifndef S_IFBLK #define S_IFBLK 0060000 #endif #ifndef S_IFDIR #define S_IFDIR 0040000 #endif #ifndef S_IFCHR #define S_IFCHR 0020000 #endif #ifndef S_IFIFO #define S_IFIFO 0010000 #endif #ifndef S_ISUID #define S_ISUID 0004000 #endif #ifndef S_ISGID #define S_ISGID 0002000 #endif #ifndef S_ISVTX #define S_ISVTX 0001000 #endif #ifndef S_ISLNK #define S_ISLNK(m) (((m) & S_IFMT) == S_IFLNK) #endif #ifndef S_ISREG #define S_ISREG(m) (((m) & S_IFMT) == S_IFREG) #endif #ifndef S_ISDIR #define S_ISDIR(m) (((m) & S_IFMT) == S_IFDIR) #endif #ifndef S_ISCHR #define S_ISCHR(m) (((m) & S_IFMT) == S_IFCHR) #endif #ifndef S_ISBLK #define S_ISBLK(m) (((m) & S_IFMT) == S_IFBLK) #endif #ifndef S_ISFIFO #define S_ISFIFO(m) (((m) & S_IFMT) == S_IFIFO) #endif #ifndef S_ISSOCK #define S_ISSOCK(m) (((m) & S_IFMT) == S_IFSOCK) #endif /* Finally ... */ extern int SignalsStatus; extern int LinuxConsole; extern int ColorMonitor; extern char cSection[]; extern char bwSection[]; #ifndef HAVE_CONST #define const #endif char temp[PATH_MAX]; char no_perm[] = "***** Permission denied ! *****"; char bad_name[] = "***** Invalid name ! *****"; char rights[16] = "-rwxrwxrwx"; #define FILE_DISPLAY_MODES 5 char FileDisplayMode[FILE_DISPLAY_MODES][15] = { "OwnerGroup", "DateTime", "Size", "Mode", "FullName" }; #define PANEL_FIELDS 17 static char PanelFields[PANEL_FIELDS][40] = { "PanelFrame", "PanelBackground", "PanelSelectedFile", "PanelSelectedFileBrightness", "PanelNotSelectedFile", "PanelNotSelectedFileBrightness", "PanelCurrentSelectedFile", "PanelCurrentNotSelectedFile", "PanelCurrentFile", "PanelPath", "PanelPathBrightness", "PanelDeviceFreeSpace", "PanelDeviceFreeSpaceBrightness", "PanelFileInfo", "PanelFileInfoBrightness", "PanelFilesInfo", "PanelFilesInfoBrightness", }; #ifdef HAVE_LINUX static int PanelColors[PANEL_FIELDS] = { WHITE, BLUE, YELLOW, ON, WHITE, ON, YELLOW, WHITE, CYAN, RED, OFF, RED, OFF, RED, OFF, BLACK, OFF }; #else static int PanelColors[PANEL_FIELDS] = { WHITE, BLACK, WHITE, ON, WHITE, OFF, WHITE, BLACK, WHITE, BLACK, OFF, BLACK, OFF, BLACK, OFF, BLACK, OFF }; #endif #define PanelFrame PanelColors[0] #define PanelBackground PanelColors[1] #define PanelSelectedFile PanelColors[2] #define PanelSelectedFileBrightness PanelColors[3] #define PanelNotSelectedFile PanelColors[4] #define PanelNotSelectedFileBrightness PanelColors[5] #define PanelCurrentSelectedFile PanelColors[6] #define PanelCurrentNotSelectedFile PanelColors[7] #define PanelCurrentFile PanelColors[8] #define PanelPath PanelColors[9] #define PanelPathBrightness PanelColors[10] #define PanelDeviceFreeSpace PanelColors[11] #define PanelDeviceFreeSpaceBrightness PanelColors[12] #define PanelFileInfo PanelColors[13] #define PanelFileInfoBrightness PanelColors[14] #define PanelFilesInfo PanelColors[15] #define PanelFilesInfoBrightness PanelColors[16] static int *UserHeartAttack; static int StartupFileDisplayMode; static int InfoDisplay = OFF; int FrameDisplay = OFF; /* Sorry, I really nead it this way ... :-( */ int tell(int); void uitclock(int); void signals(int); void fatal(char *); void ignore_signals(void); void restore_signals(void); #define max(a, b) ((a) >= (b) ? (a) : (b)) #define min(a, b) ((a) <= (b) ? (a) : (b)) static void xchg(int *a, int *b) { int tmp = *a; *a = *b; *b = tmp; } panel *panel_init(int _lines, int _columns, int _begin_x, int _begin_y, char *_path, edit *_edt, int *_UserHeartAttack, configuration *config) { char *data, *section; int sectionptr, index, i; static int configured; panel *this = (panel *)malloc(sizeof(panel)); this->lines = _lines; this->columns = _columns; this->begin_x = _begin_x; this->begin_y = _begin_y; this->focus = OFF; this->edt = _edt; this->info_dirty = ON; this->entries = this->selected_files = 0; this->last_index = -1; this->mode = this->found_selection = this->end = 0; this->current_entry = 0; this->first_on_screen = max(0, this->current_entry - (this->lines-2) + 1); this->on_screen = MAX_ENTRIES; UserHeartAttack = _UserHeartAttack; memset(this->dir_entry, 0, sizeof(this->dir_entry)); strcpy(this->path, _path); this->win = window_init(this->begin_x, this->begin_y, this->lines, this->columns); if (configured) { this->mode = StartupFileDisplayMode; return this; } if (configuration_getstatus(config) == STATUS_OK) { sectionptr = configuration_getsectionptr(config, "[UIT-Setup]"); if (sectionptr != -1) { configuration_getfielddata(config, sectionptr, "StartupFileDisplayMode", &data, 1, DO_SEEK); if (data) { for (i = 0; i < FILE_DISPLAY_MODES; i++) if (strcmp(data, FileDisplayMode[i]) == 0) break; if (i == FILE_DISPLAY_MODES) fprintf(stderr, "Invald StartupFileDisplayMode (%s).\n", data); else this->mode = StartupFileDisplayMode = i; } configuration_getfielddata(config, sectionptr, "InfoDisplay", &data, 1, DO_SEEK); InfoDisplay = data && strcmp(data, "ON") == 0; configuration_getfielddata(config, sectionptr, "FrameDisplay", &data, 1, DO_SEEK); FrameDisplay = data && strcmp(data, "ON") == 0; } section = (LinuxConsole && ColorMonitor) ? cSection : bwSection; sectionptr = configuration_getsectionptr(config, section); if (sectionptr != -1) for (i = 0; i < PANEL_FIELDS; i++) { configuration_getfielddata(config, sectionptr, PanelFields[i], &data, 1, DO_SEEK); if (!data || (index = tty_getcolorindex(data)) == -1) fprintf(stderr, "Invalid %s (%s).\n", PanelFields[i],data); else PanelColors[i] = index; } } configured = 1; return this; } void panel_end(panel *this) { int i; if (this->dir) closedir(this->dir); for (i = 0; i < this->entries; i++) if (this->dir_entry[i].name) free(this->dir_entry[i].name); free(this->win); } char *cutname(char *name, int which, int how) { static char tname[2][16]; if (how) { memset(tname[which], ' ', 14); tname[which][14] = 0; return memcpy(tname[which], name, min(strlen(name), 14)); } else return strncpy(tname[which], name, 14); } static int sortfn(const void *_first, const void *_second) { int retval; _dir_entry *first = (_dir_entry *)_first; _dir_entry *second = (_dir_entry *)_second; int first_is_dir = first->type == DIR_ENTRY; int second_is_dir = second->type == DIR_ENTRY; char *pfirst = strrchr(first->name , '.'); char *psecond = strrchr(second->name, '.'); if (first_is_dir == second_is_dir) if (pfirst && psecond) return (retval = strcmp(++pfirst, ++psecond)) ? retval : strcmp(first->name, second->name); else return (pfirst || psecond) ? (pfirst ? -1 : 1) : strcmp(first->name, second->name); else return first_is_dir ? -1 : 1; } void panel_nooptimizations(panel *this) { this->on_screen = MAX_ENTRIES; } char *panel_getcurrentfilename(panel *this) { return this->dir_entry[this->current_entry].name; } int panel_getcurrentfileuid(panel *this) { return this->dir_entry[this->current_entry].uid; } int panel_getcurrentfilegid(panel *this) { return this->dir_entry[this->current_entry].gid; } int panel_getcurrentfilemode(panel *this) { return this->dir_entry[this->current_entry].mode; } int panel_getcurrentfiletype(panel *this) { return this->dir_entry[this->current_entry].type; } void panel_recover(panel *this) { char msg[PATH_MAX]; this->first_on_screen = this->current_entry = 0; sprintf(msg, "Can't get permission in directory %s ! (press any key).", this->path); status(msg, 1, 1, 1, MSG_ERROR); if (strcmp(this->path, "/") == 0) fatal("can't get permission in root directory"); strcpy(this->path, "/"); chdir(this->path); panel_action(this, act_REFRESH, NULL, NULL, 1); } int panel_getdirinfo(panel *this, char *directory, int verify) { DIR *tmpdir; struct dirent *d; struct stat s; #ifdef HAVE_STATFS struct statfs fstat; #endif struct passwd *pwd; struct group *grp; struct tm *time; int i, j, old_uid = -1, old_gid = -1, msdosfs = 0, namelen; int old_entries = 0, backdir_index = -1, sz, hour; _dir_entry *old_dir_entry = NULL, tmp; char old_path[PATH_MAX], temp[PATH_MAX]; if (!(tmpdir = opendir(directory))) return 0; if (chdir(directory) == -1) { closedir(tmpdir); return 0; } if (this->dir) closedir(this->dir); this->dir = tmpdir; strcpy(old_path, this->path); getcwd(this->path, PATH_MAX); #ifdef HAVE_STATFS statfs(".", &fstat); msdosfs = fstat.f_type == 0x4d44; /* I can't get this number without */ /* including linux/msdos_fs.h */ #endif if (verify = (verify && this->selected_files && strcmp(old_path, this->path) == 0)) { if (!(old_dir_entry = (_dir_entry *)malloc(sizeof(this->dir_entry)))) fatal("not enough memory"); memcpy(old_dir_entry, this->dir_entry, (old_entries = this->entries) * sizeof(_dir_entry)); memset(this->dir_entry, 0, sizeof(this->dir_entry)); } this->selected_files = 0; this->maxname = 0; for (this->entries = 0; (d = readdir(this->dir)) && this->entries < MAX_ENTRIES; this->entries++) { if (d->d_name[0] == '.' && !d->d_name[1]) { this->entries--; continue; } /* ignore "." */ if (d->d_name[0] == '.' && d->d_name[1] == '.' && d->d_name[2] == 0) if (this->path[1]) backdir_index = this->entries; else { this->entries--; continue; } /* ignore ".." if root dir */ s.st_ino = 0; stat(d->d_name, &s); this->dir_entry[this->entries].mode = s.st_mode; this->dir_entry[this->entries].uid = s.st_uid; this->dir_entry[this->entries].gid = s.st_gid; if (verify) { for (j = 0; j < old_entries; j++) if (strcmp(d->d_name, old_dir_entry[j].name) == 0) { this->selected_files += (this->dir_entry[this->entries].selected = old_dir_entry[j].selected); break; } } else this->dir_entry[this->entries].selected = 0; if (s.st_ino) { if (S_ISDIR(s.st_mode)) this->dir_entry[this->entries].type = DIR_ENTRY; else { if (S_ISREG(s.st_mode)) { this->dir_entry[this->entries].type = FILE_ENTRY; #ifdef HAVE_LINUX /* * with the new Linux kernel (0.99.13) all MSDOS * files have are "executables", so, when working with * msdos file systems, we have to ignore those bits. */ this->dir_entry[this->entries].executable = ((s.st_mode & 0111) && !msdosfs) ? 1 : 0; #else this->dir_entry[this->entries].executable = (s.st_mode & 0111) ? 1 : 0; #endif } else { if (S_ISFIFO(s.st_mode)) this->dir_entry[this->entries].type = FIFO_ENTRY; else if (S_ISSOCK(s.st_mode)) this->dir_entry[this->entries].type = SOCKET_ENTRY; else this->dir_entry[this->entries].type = FILE_ENTRY; this->dir_entry[this->entries].executable = OFF; } } this->dir_entry[this->entries].size = s.st_size; } else { this->dir_entry[this->entries].type = SYMLINK_ENTRY; sz = readlink(d->d_name, temp, PATH_MAX); this->dir_entry[this->entries].size = (sz == -1) ? 0 : sz; } if (s.st_uid == old_uid) memcpy(this->dir_entry[this->entries].owner, this->dir_entry[this->entries - 1].owner, 7); else { pwd = getpwuid(old_uid = s.st_uid); if (pwd) sprintf(this->dir_entry[this->entries].owner, "%-7s", pwd->pw_name); else sprintf(this->dir_entry[this->entries].owner, "%-7d",s.st_uid); } if (s.st_gid == old_gid) memcpy(this->dir_entry[this->entries].group, this->dir_entry[this->entries - 1].group, 7); else { grp = getgrgid(old_gid = s.st_gid); if (grp) sprintf(this->dir_entry[this->entries].group, "%-7s", grp->gr_name); else sprintf(this->dir_entry[this->entries].group, "%-7d",s.st_gid); } time = localtime(&s.st_mtime); if ((hour = time->tm_hour % 12) == 0) hour = 12; sprintf(this->dir_entry[this->entries].date,"%2d-%02d-%02d %2d:%02d%c", time->tm_mon + 1, time->tm_mday, time->tm_year, hour, time->tm_min, (time->tm_hour < 12) ? 'a' : 'p'); if (this->dir_entry[this->entries].name) free(this->dir_entry[this->entries].name); if (!(this->dir_entry[this->entries].name = (char *)malloc((namelen = strlen(d->d_name)) + 1))) fatal("not enough memory"); strcpy(this->dir_entry[this->entries].name, d->d_name); this->maxname = max(this->maxname, namelen); } if (this->entries == MAX_ENTRIES && readdir(this->dir)) { sprintf(temp, "Too many directory entries!\ Only %d will be displayed. (press any key)", MAX_ENTRIES); status(temp, 1, 1, 1, MSG_ERROR); } if (verify) { for (i = 0; i < old_entries; i++) if (old_dir_entry[i].name) free(old_dir_entry[i].name); free(old_dir_entry); } if (backdir_index != -1) { tmp = this->dir_entry[0]; this->dir_entry[0] = this->dir_entry[backdir_index]; this->dir_entry[backdir_index] = tmp; qsort(this->dir_entry + 1, this->entries - 1, sizeof(_dir_entry), sortfn); } else qsort(this->dir_entry, this->entries, sizeof(_dir_entry), sortfn); return 1; } int panel_getnext(panel *this) { int i, retval; if (this->end) { this->end = 0; return -1; } for (i = this->last_index + 1; i < this->entries; i++) if (this->dir_entry[i].selected) { this->found_selection = 1; return this->last_index = i; } this->end = !this->found_selection; retval = this->found_selection ? -1 : this->current_entry; this->last_index = -1; this->found_selection = 0; return (retval == this->current_entry && this->dir_entry[this->current_entry].type == DIR_ENTRY) ? -1 : retval; } void panel_update(panel *this) { int i, limit; tty_status status; tty_save(&status); for (i = this->first_on_screen; i < this->entries && (i - this->first_on_screen < this->lines - 2); i++) panel_update_entry(this, i); tty_background(PanelBackground); memset(temp, ' ', this->columns); limit = min(this->lines - 2, this->on_screen); for (; i < limit; i++) { window_cursormove(this->win, i - this->first_on_screen + 1, 1); window_write(temp, this->columns - 2); } this->on_screen = this->entries; tty_restore(&status); } void panel_trunc_fname(char *fname, char *dest, int len) { int flen; if ((flen = strlen(fname)) > len) { dest[0] = dest[1] = dest[2] = '.'; memcpy(dest + 3, fname + flen - len + 3, len - 3); } else memcpy(dest, fname, flen); } void panel_update_path(panel *this) { char *t; int i, len; memset(temp, ' ', this->columns); panel_trunc_fname(this->path, t = temp, len = this->columns - 4 - 11); for (i = 0; i < len; i++) if (!is_print(t[i])) t[i] = '?'; tty_bright(PanelPathBrightness); tty_foreground(PanelPath); tty_background(PanelFrame); window_cursormove(this->win, 0, 2); window_write(temp, len); } void panel_update_size(panel *this) { #ifdef HAVE_STATFS struct statfs fsbuf; char sz[16]; statfs(this->path, &fsbuf); sprintf(sz, "%10d", fsbuf.f_bfree * fsbuf.f_bsize); tty_bright(PanelDeviceFreeSpaceBrightness); tty_foreground(PanelDeviceFreeSpace); tty_background(PanelFrame); window_cursormove(this->win, 0, this->columns - 2 - 10); window_write(sz, strlen(sz)); #endif } void panel_update_info(panel *this) { char str[256], temp_rights[16], temp_name[16], *t; int i, total_size = 0, total_files = 0, index, mode, len, maxname; if (this->selected_files) { while((index = panel_getnext(this)) != -1) { total_files ++; total_size += this->dir_entry[index].size; } sprintf(str, "%d bytes in %d file(s)", total_size, total_files); tty_bright(PanelFilesInfoBrightness); tty_foreground(PanelFilesInfo); this->info_dirty = ON; } else { if (InfoDisplay == OFF) if (this->info_dirty) { *str = 0; this->info_dirty = 0; goto display_info; } else return; strcpy(temp_rights, rights); mode = this->dir_entry[this->current_entry].mode; if (S_ISLNK(mode)) temp_rights[0] = 'l'; if (S_ISDIR(mode)) temp_rights[0] = 'd'; if (S_ISCHR(mode)) temp_rights[0] = 'c'; if (S_ISBLK(mode)) temp_rights[0] = 'b'; if (S_ISFIFO(mode)) temp_rights[0] = 'p'; if (S_ISSOCK(mode)) temp_rights[0] = 's'; for (i = 0; i < 9; mode >>= 1, i++) if (!(mode & 1)) temp_rights[9 - i] = '-'; mode = this->dir_entry[this->current_entry].mode; if (mode & S_ISUID) temp_rights[3] = (temp_rights[3] == 'x') ? 's' : 'S'; if (mode & S_ISGID) temp_rights[6] = (temp_rights[6] == 'x') ? 's' : 'S'; if (mode & S_ISVTX) temp_rights[9] = (temp_rights[9] == 'x') ? 't' : 'T'; maxname = this->columns - 26; len = min(strlen(this->dir_entry[this->current_entry].name), maxname); memcpy(t = str, this->dir_entry[this->current_entry].name, len); for (i = 0; i < len; i++) if (!is_print(t[i])) t[i] = '?'; memset(str + len, ' ', maxname - len); if (this->dir_entry[this->current_entry].type == DIR_ENTRY) sprintf(str + maxname, " %10s %10s", (strcmp(this->dir_entry[this->current_entry].name, "..") == 0) ? "UP--DIR" : "SUB-DIR", temp_rights); else sprintf(str + maxname, " %10d %10s", this->dir_entry[this->current_entry].size, temp_rights); display_info: tty_bright(PanelFileInfoBrightness); tty_foreground(PanelFileInfo); } memcpy(temp, str, len = strlen(str)); memset(temp + len, ' ', this->columns - 2 - len); tty_background(PanelFrame); window_cursormove(this->win, this->lines - 1, 2); window_write(temp, this->columns - 4); } void panel_update_entry(panel *this, int entry) { int len, i, mode, reserved; char buf[256], temp_rights[16], *t; int foreground, background, bright; memset(temp, ' ', this->columns); reserved = (this->mode == 4) ? 4 : 20; len = min(strlen(this->dir_entry[entry].name), (unsigned)this->columns - reserved); memcpy(t = &temp[1], this->dir_entry[entry].name, len); for (i = 0; i < len; i++) if (!is_print(t[i])) t[i] = '?'; if (len == (unsigned)this->columns - reserved) len--; if (entry || this->path[1] == 0) switch (this->dir_entry[entry].type) { case DIR_ENTRY: temp[len + 1] = '/'; break; case FILE_ENTRY: if (this->dir_entry[entry].executable) temp[len + 1] = '*'; break; case SYMLINK_ENTRY: temp[len + 1] = '@'; break; case FIFO_ENTRY: temp[len + 1] = '|'; break; case SOCKET_ENTRY: temp[len + 1] = '='; break; } switch (this->mode) { case 0: memcpy(temp + this->columns - 2 - 16, this->dir_entry[entry].owner, 7); memcpy(temp + this->columns - 2 - 8, this->dir_entry[entry].group, 7); break; case 1: memcpy(temp + this->columns - 2 - 16, this->dir_entry[entry].date, 15); break; case 2: sprintf(buf, "%10d", this->dir_entry[entry].size); memcpy(temp + this->columns - 2 - 11, buf, 10); break; case 3: strcpy(temp_rights, rights); mode = this->dir_entry[entry].mode; if (S_ISLNK(mode)) temp_rights[0] = 'l'; if (S_ISDIR(mode)) temp_rights[0] = 'd'; if (S_ISCHR(mode)) temp_rights[0] = 'c'; if (S_ISBLK(mode)) temp_rights[0] = 'b'; if (S_ISFIFO(mode)) temp_rights[0] = 'p'; if (S_ISSOCK(mode)) temp_rights[0] = 's'; for (i = 0; i < 9; mode >>= 1, i++) if (!(mode & 1)) temp_rights[9 - i] = '-'; mode = this->dir_entry[entry].mode; if (mode & S_ISUID) temp_rights[3] = (temp_rights[3] == 'x') ? 's' : 'S'; if (mode & S_ISGID) temp_rights[6] = (temp_rights[6] == 'x') ? 's' : 'S'; if (mode & S_ISVTX) temp_rights[9] = (temp_rights[9] == 'x') ? 't' : 'T'; memcpy(temp + this->columns - 2 - 11, temp_rights, 10); break; case 4: break; default: fatal("invalid mode"); } if (entry == this->current_entry && this->focus == ON) { foreground = this->dir_entry[entry].selected ? PanelCurrentSelectedFile : PanelCurrentNotSelectedFile; background = PanelCurrentFile; } else { foreground = this->dir_entry[entry].selected ? PanelSelectedFile : PanelNotSelectedFile; background = PanelBackground; } bright = this->dir_entry[entry].selected ? PanelSelectedFileBrightness : PanelNotSelectedFileBrightness; tty_foreground(foreground); tty_background(background); tty_bright(bright); window_cursormove(this->win, entry - this->first_on_screen + 1, 1); window_write(temp, this->columns - 2); } void panel_update_frame(panel *this) { int i; tty_status status; char buf[256]; tty_save(&status); tty_foreground(PanelFrame); tty_bright(OFF); tty_reverse(ON); if (FrameDisplay == ON) { for (i = 0; i < this->lines; i++) { window_cursormove(this->win, i, 0); tty_putch(' '); } for (i = 0; i < this->lines; i++) { window_cursormove(this->win, i, this->columns - 1); tty_putch(' '); } } memset(buf, ' ', 256); window_cursormove(this->win, 0, 0); tty_write(buf, this->columns); window_cursormove(this->win, this->lines - 1, 0); tty_write(buf, this->columns); tty_restore(&status); } void panel_setfocus(panel *this, int status, panel *link) { this->focus = status; panel_update_entry(this, this->current_entry); if (this->focus) if (chdir(this->path) == -1) panel_recover(this); } char *panel_getpath(panel *this, char *temppath, unsigned len) { panel_trunc_fname(this->path, temppath, len); temppath[min(len, strlen(this->path))] = 0; return temppath; } int filelength(int handle) { int temp, length; temp = tell(handle); lseek(handle, 0, SEEK_END); length = tell(handle); lseek(handle, temp, SEEK_SET); return length; } #define COPY_BUFFER_SIZE (128*1024) enum { SD_OK, SD_CANCEL, S_OPENERR, S_READERR, D_CREATERR, D_WRITEERR, SD_NOSPACE, SD_NOMEM, }; char copyerr[8][50] = { " ", " ", "can't open source file !", "can't read from source file !", "can't create destination file !", "can't write to destination file !", "not enough free disk space !", "not enough memory !", }; int panel_copy(panel *this, char *src, char *dest, int mode) { char *buf; char msg[PATH_MAX]; int shandle, dhandle, i, len, err, key, memsize, bytes_to_transfer; for (buf = dest + strlen(dest); *buf != '/'; buf--); buf++; if (this->chkdest && !access(dest, 0)) { if (this->selected_files) sprintf(msg, "(COPY) Destination file %s exists.\ (Overwrite/Skip/All/Cancel) ?", cutname(buf, 0, 0)); else sprintf(msg, "(COPY) Destination file %s exists.\ (Overwrite/Skip/Cancel) ?", cutname(buf, 0, 0)); switch (key = status(msg, 1, 1, 1, MSG_ERROR)) { case KEY_ENTER: case 'O': case 'o': break; case 'a': case 'A': this->chkdest = OFF; break; case 's': case 'S': return SD_OK; default: return SD_CANCEL; } } sprintf(msg, "(COPY) Copying %s to %s", cutname(src, 0, 1), cutname(buf, 1, 1)); status(msg, 0, 0, 0, MSG_WARNING); if ((shandle = open(src, O_RDONLY)) == -1) return S_OPENERR; if ((dhandle = creat(dest, mode)) == -1) { close(shandle); return D_CREATERR; } memsize = min(len = filelength(shandle), COPY_BUFFER_SIZE); if (S_ISBLK(mode) || S_ISCHR(mode)) { len = MAXINT; memsize = COPY_BUFFER_SIZE; } if (len == 0) { close(shandle); close(dhandle); return SD_OK; } if ((buf = (char *)malloc(memsize)) == NULL) { close(shandle); close(dhandle); remove(dest); return SD_NOMEM; } for (i = 0; i < len; i += COPY_BUFFER_SIZE) { bytes_to_transfer = min(len - i, memsize); err = read(shandle, buf, bytes_to_transfer); if (err != bytes_to_transfer) if (err >= 0) { if (err) bytes_to_transfer = err; else { close(shandle); close(dhandle); free(buf); return SD_OK; } } else { close(shandle); close(dhandle); free(buf); remove(dest); return S_READERR; } err = write(dhandle, buf, bytes_to_transfer); if (err != bytes_to_transfer) if (err >= 0) { close(shandle); close(dhandle); free(buf); remove(dest); return SD_NOSPACE; } else { close(shandle); close(dhandle); free(buf); remove(dest); return (errno == ENOSPC) ? SD_NOSPACE : D_WRITEERR; } } close(shandle); close(dhandle); free(buf); return SD_OK; } enum { FT_OK, FT_CANCEL, T_CREATERR, F_DELETERR, }; char moveerr[7][50] = { " ", " ", "can't create destination file !", "can't delete source file !", }; int panel_move(panel *this, char *from, char *to) { int key; char *buf; char msg[PATH_MAX]; for (buf = to + strlen(to); *buf != '/'; buf--); buf++; if (this->chkdest && !access(to, 0)) { if (this->selected_files) sprintf(msg, "(MOVE) Destination file %s exists.\ (Overwrite/Skip/All/Cancel) ?", cutname(buf, 0, 0)); else sprintf(msg, "(MOVE) Destination file %s exists.\ (Overwrite/Skip/Cancel) ?", cutname(buf, 0, 0)); switch (key = status(msg, 1, 1, 1, MSG_ERROR)) { case KEY_ENTER: case 'O': case 'o': break; case 'a': case 'A': this->chkdest = OFF; break; case 's': case 'S': return FT_OK; default: return FT_CANCEL; } } sprintf(msg, "(MOVE) Moving %s to %s", cutname(from, 0, 1), cutname(buf, 1, 1)); status(msg, 0, 0, 0, MSG_WARNING); unlink(to); if (link(from, to) == -1) return T_CREATERR; if (unlink(from) == -1) return F_DELETERR; return FT_OK; } int panel_verify_name(char *file_name) { char *ptr; for (ptr = file_name; *ptr; ptr++) if (*ptr == '/') { status(bad_name, 1, 1, 1, MSG_ERROR); return 0; } return 1; } int panel_getindex(panel *this, char *str) { int i; str[this->maxname] = 0; for (i = 0; i < this->entries && strcmp(str,this->dir_entry[i].name); i++); if (i == this->entries) { for (i = 0; i < this->entries && strcasecmp(str, this->dir_entry[i].name); i++); if (i == this->entries) return 0; } return i; } int panel_action(panel *this, int action, panel *link, void *aux_info, int repeat_count) { int need_update, need_update_all, old_current_entry; int done = 0, i, err = 0, entry, len, back; int rename_dir = 0, verify = aux_info == (void *)-1; char oldentry[256], msg[PATH_MAX], text[PATH_MAX]; char oldpath[PATH_MAX], *ptr; switch (action) { case act_ENTER: switch (this->dir_entry[this->current_entry].type) { case DIR_ENTRY: if ((strcmp(this->dir_entry[this->current_entry].name, "..") == 0) && (strcmp(this->path, "/") == 0)) break; back = strcmp(this->dir_entry[this->current_entry].name, "..") ? 0 : 1; strcpy(oldpath, this->path); if (!panel_getdirinfo(this, this->dir_entry[this->current_entry].name, ON)) { if (back) panel_recover(this); else status(no_perm, 1, 1, 1, MSG_ERROR); break; } if (back) { for (ptr = oldpath + strlen(oldpath) - 1; *ptr != '/'; ptr--); ptr++; for (i = 0; strcmp(this->dir_entry[i].name, ptr) && i < this->entries; i++); this->current_entry = i; this->first_on_screen = max(0, this->current_entry - (this->lines - 2) + 1); } else this->current_entry = this->first_on_screen = 0; panel_update_path(this); panel_update(this); panel_update_size(this); if (strcmp(this->path, link->path) == 0) panel_action(link, act_REFRESH, this, (void *)-1, 1); panel_update_size(link); done = 1; break; case FILE_ENTRY: if (this->dir_entry[this->current_entry].executable) { uitclock(OFF); tty_end(); tty_putscreen((char *)aux_info); sprintf(text, "\"%s\"", this->dir_entry[this->current_entry].name); restore_signals(); signals_dfl(); system(text); signals(SignalsStatus); ignore_signals(); write(1, "\n\n", 2); tty_init(); panel_nooptimizations(this); panel_nooptimizations(link); edit_puts(this->edt, this->dir_entry[this->current_entry].name); done = -1; } break; } break; case act_COPY: this->chkdest = ON; if (!this->selected_files) { signals(OFF); if (this->dir_entry[this->current_entry].type == DIR_ENTRY) break; sprintf(msg, "(COPY) Copying file %s to : ", cutname(this->dir_entry[this->current_entry].name,0,0)); strcpy(oldpath, link->path); strcat(oldpath, "/"); strcat(oldpath, this->dir_entry[this->current_entry].name); if (!edit_gets(this->edt, msg, text, oldpath)) break; strcpy(oldpath, this->path); strcat(oldpath, "/"); strcat(oldpath, this->dir_entry[this->current_entry].name); if (strcmp(text, oldpath) == 0) { sprintf(msg, "(COPY) Can't copy file %s to itself! (press any key)", cutname(this->dir_entry[this->current_entry].name,0,0)); status(msg, 1, 1, 1, MSG_ERROR); break; } err=panel_copy(this,this->dir_entry[this->current_entry].name, text,this->dir_entry[this->current_entry].mode); if (err != SD_OK) { if (err == SD_CANCEL) break; sprintf(msg, "(COPY) Error copying file %s : %s", cutname(this->dir_entry[this->current_entry].name, 0, 1), copyerr[err]); status(msg, 1, 1, 1, MSG_ERROR); break; } status(NULL, 0, 0, 1, MSG_OK); panel_update_size(this); panel_update_size(link); } else { signals(OFF); if (strcmp(this->path, link->path) == 0) { status("(COPY) Can't do it! (press any key)", 1, 1, 1, MSG_ERROR); break; } if (status("(COPY) Copying file(s) ... \ (ENTER to continue, TAB to cancel)", 1, 0, 1, MSG_WARNING) != KEY_ENTER) break; strcpy(temp, link->path); strcat(temp, "/"); len = strlen(temp); signals(ON); while ((entry = panel_getnext(this)) != -1) { strcpy(temp + len, this->dir_entry[entry].name); if (*UserHeartAttack) { *UserHeartAttack = 0; status("Operation aborted. (press any key)", 1, 1, 1, MSG_ERROR); break; } err = panel_copy(this, this->dir_entry[entry].name, temp, this->dir_entry[entry].mode); if (err != SD_OK) { if (err == SD_CANCEL) break; sprintf(msg, "(COPY) Error copying file %s : %s", cutname(this->dir_entry[entry].name, 0, 1), copyerr[err]); if (status(msg, 1, 1, 0, MSG_ERROR) != KEY_ENTER) break; } else this->dir_entry[entry].selected = 0; panel_update_size(this); panel_update_size(link); } status(NULL, 0, 0, 1, MSG_OK); } signals(OFF); if (!panel_getdirinfo(link, link->path, ON)) panel_recover(link); else { panel_update(link); panel_update_info(link); } if (!panel_getdirinfo(this, this->path, ON)) panel_recover(this); else { panel_update(this); panel_update_info(this); } break; case act_DELETE: if (!this->selected_files && this->dir_entry[this->current_entry].type == DIR_ENTRY) { signals(OFF); if (strcmp(this->dir_entry[this->current_entry].name, "..")==0) break; sprintf(msg, "(RMDIR) Deleting directory %s ?\ (ENTER to continue, TAB to cancel)", cutname(this->dir_entry[this->current_entry].name, 0, 0)); if (status(msg, 1, 0, 1, MSG_ERROR) != KEY_ENTER) break; strcpy(temp, this->path); strcat(temp, "/"); strcat(temp, this->dir_entry[this->current_entry].name); if (rmdir(this->dir_entry[this->current_entry].name) == -1 && unlink(this->dir_entry[this->current_entry].name) == -1) { sprintf(msg, "(RMDIR) Can't remove directory %s !", cutname(this->dir_entry[this->current_entry].name, 0, 0)); status(msg, 1, 1, 0, MSG_ERROR); status(NULL, 0, 0, 1, MSG_OK); break; } else if (strcmp(temp, link->path) == 0) { strcpy(link->path, this->path); panel_action(link, act_REFRESH, this, NULL, 1); } panel_update_size(this); panel_update_size(link); } else { signals(OFF); if (status("(DEL) Deleting file(s) ... \ (ENTER to continue, TAB to cancel)", 1, 0, 1, MSG_ERROR) != KEY_ENTER) break; for (i = 0; i < this->entries; i++) if (this->dir_entry[i].selected) break; signals(ON); while ((entry = panel_getnext(this)) != -1) { sprintf(msg, "(DEL) Deleting %s", cutname(this->dir_entry[entry].name, 0, 1)); status(msg, 0, 0, 0, MSG_ERROR); if (*UserHeartAttack) { *UserHeartAttack = 0; status("Operation aborted. Not too late I hope ... \ (press any key)", 1, 1, 1, MSG_ERROR); break; } if (unlink(this->dir_entry[entry].name) == -1) { sprintf(msg, "(DEL) Can't delete file %s ! \ (press any key)", cutname(this->dir_entry[entry].name, 0, 1)); if (status(msg, 1, 1, 1, MSG_ERROR) != KEY_ENTER) break; } else this->dir_entry[entry].selected = 0; } if (i != this->entries) this->current_entry = i; signals(OFF); panel_update_size(this); panel_update_size(link); status(NULL, 0, 0, 1, MSG_OK); } signals(OFF); if (!panel_getdirinfo(this, this->path, ON)) panel_recover(this); else { this->current_entry = min(this->current_entry,this->entries-1); this->first_on_screen = max(0, this->current_entry - (this->lines - 2) + 1); panel_update(this); panel_update_info(this); } if (strcmp(this->path, panel_getpath(link, temp, PATH_MAX)) == 0) { if (!panel_getdirinfo(link, link->path, ON)) panel_recover(link); else { link->current_entry = min(link->current_entry, link->entries - 1); link->first_on_screen = max(0, link->current_entry - (link->lines - 2) + 1); panel_update(link); panel_update_info(link); } } break; case act_SELECT: if (this->dir_entry[this->current_entry].type != DIR_ENTRY) { this->dir_entry[this->current_entry].selected++; this->selected_files += this->dir_entry[this->current_entry].selected ? 1 : -1; panel_update_entry(this, this->current_entry); } panel_action(this, act_DOWN, link, NULL, repeat_count); break; case act_SELECTALL: for (i = 0; i < this->entries; i++) if (this->dir_entry[i].type != DIR_ENTRY) { this->dir_entry[i].selected = ON; this->selected_files++; } panel_update(this); done = 1; break; case act_UNSELECTALL: for (i = 0; i < this->entries; i++) this->dir_entry[i].selected = OFF; this->selected_files = 0; panel_update(this); done = 1; break; case act_TOGGLE: for (i = 0; i < this->entries; i++) if (this->dir_entry[i].type != DIR_ENTRY) this->dir_entry[i].selected = !this->dir_entry[i].selected; this->selected_files = this->entries - this->selected_files; panel_update(this); done = 1; break; case act_MKDIR: if (!edit_gets(this->edt, "(MKDIR) Enter directory name : ", temp, NULL)) break; if (!temp[0] || !panel_verify_name(temp)) break; if (mkdir(temp, S_IFDIR | S_IRWXU | S_IRWXG) == -1) { status(no_perm, 1, 1, 1, MSG_ERROR); break; } if (!panel_getdirinfo(this, this->path, ON)) panel_recover(this); else { this->current_entry = panel_getindex(this, temp); this->first_on_screen = max(0, this->current_entry - (this->lines - 2) + 1); panel_update(this); panel_update_info(this); panel_update_size(this); } if (strcmp(this->path, panel_getpath(link, temp, PATH_MAX)) == 0) { if (!panel_getdirinfo(link, link->path, ON)) panel_recover(link); else { panel_update(link); panel_update_info(link); } } panel_update_size(link); break; case act_MOVE: this->chkdest = ON; if (!this->selected_files && (this->dir_entry[this->current_entry].type != DIR_ENTRY)) { signals(OFF); sprintf(msg, "(MOVE) Moving file %s to : ", cutname(this->dir_entry[this->current_entry].name,0,0)); strcpy(oldpath, link->path); strcat(oldpath, "/"); strcat(oldpath, this->dir_entry[this->current_entry].name); if (!edit_gets(this->edt, msg, text, oldpath)) break; strcpy(oldpath, this->path); strcat(oldpath, "/"); strcat(oldpath, this->dir_entry[this->current_entry].name); if (strcmp(text, oldpath) == 0) { sprintf(msg, "(MOVE) Can't move file %s to itself! \ (press any key)", cutname(this->dir_entry[this->current_entry].name, 0, 0)); status(msg, 1, 1, 1, MSG_ERROR); break; } if ((err=panel_move(this, this->dir_entry[this->current_entry].name, text)) != FT_OK) { if (err == FT_CANCEL) break; sprintf(msg, "(MOVE) Error moving file %s : %s", cutname(this->dir_entry[this->current_entry].name, 0, 1), moveerr[err]); status(msg, 1, 1, 1, MSG_ERROR); } status(NULL, 0, 0, 1, MSG_OK); panel_update_size(this); panel_update_size(link); } else { if (!this->selected_files && this->dir_entry[this->current_entry].type == DIR_ENTRY) { signals(OFF); if (strcmp(this->dir_entry[this->current_entry].name, "..") == 0) break; if (!edit_gets(this->edt, "(RENAME) New directory name : ", text, NULL) || !text[0]) break; if (rename(this->dir_entry[this->current_entry].name, text) == -1) { sprintf(msg, "(RENAME) Error renaming directory : %s !\ (press any key)", cutname(this->dir_entry[this->current_entry].name, 0, 0)); status(msg, 1, 1, 1, MSG_ERROR); break; } rename_dir = 1; status(NULL, 0, 0, 1, MSG_OK); } else { signals(OFF); if (strcmp(this->path, link->path) == 0) { status("(MOVE) Can't do it! (press any key)", 1, 1, 1, MSG_ERROR); break; } if (status("(MOVE) Moving file(s) ... \ (ENTER to continue, TAB to cancel)", 1, 0, 1, MSG_WARNING) != KEY_ENTER) break; strcpy(temp, link->path); strcat(temp, "/"); len = strlen(temp); signals(ON); while ((entry = panel_getnext(this)) != -1) { strcpy(temp + len, this->dir_entry[entry].name); if (*UserHeartAttack) { *UserHeartAttack = 0; status("Operation aborted. (press any key)", 1, 1, 1, MSG_ERROR); break; } if ((err = panel_move(this,this->dir_entry[entry].name, temp)) != FT_OK) { if (err == FT_CANCEL) break; sprintf(msg, "(MOVE) Error moving file %s : %s", cutname(this->dir_entry[entry].name, 0, 1), moveerr[err]); if (status(msg, 1, 1, 0, MSG_ERROR) != KEY_ENTER) break; } else this->dir_entry[entry].selected = 0; } signals(OFF); status(NULL, 0, 0, 1, MSG_OK); } } signals(OFF); if (!panel_getdirinfo(link, link->path, ON)) panel_recover(link); else { link->current_entry = min(link->current_entry, link->entries - 1); link->first_on_screen = max(0, link->current_entry - (link->lines - 2) + 1); panel_update(link); panel_update_info(link); panel_update_size(link); } if (!panel_getdirinfo(this, this->path, ON)) panel_recover(this); else { this->current_entry = rename_dir ? panel_getindex(this, text) : min(this->current_entry, this->entries - 1); this->first_on_screen = max(0, this->current_entry - (this->lines - 2) + 1); panel_update(this); panel_update_info(this); panel_update_size(this); } break; case act_UP: need_update_all = need_update = 0; while (repeat_count--) { if (this->current_entry == 0) break; if (this->current_entry == this->first_on_screen) { this->current_entry--; this->first_on_screen--; need_update_all = 1; } else { this->current_entry--; if (!need_update) panel_update_entry(this, this->current_entry + 1); need_update = 1; } } if (need_update_all) panel_update(this); else if (need_update) panel_update_entry(this, this->current_entry); else done = -1; break; case act_DOWN: need_update_all = need_update = 0; while (repeat_count--) { if (this->current_entry < this->entries - 1) this->current_entry++; else break; if (this->current_entry - this->first_on_screen >= this->lines - 2) { this->first_on_screen++; need_update_all = 1; continue; } if (!need_update) panel_update_entry(this, this->current_entry - 1); need_update = 1; } if (need_update_all) panel_update(this); else if (need_update) panel_update_entry(this, this->current_entry); else done = -1; break; case act_PGUP: if (this->current_entry == 0) { done = -1; break; } old_current_entry = this->current_entry; if (this->current_entry < this->lines - 2) this->current_entry = this->first_on_screen = 0; else { this->current_entry -= this->lines - 2; this->first_on_screen = max(0, this->first_on_screen - (this->lines - 2)); } if (this->entries > this->lines - 2) panel_update(this); else { panel_update_entry(this, old_current_entry); panel_update_entry(this, this->current_entry); } break; case act_PGDOWN: if (this->current_entry == this->entries - 1) { done = -1; break; } old_current_entry = this->current_entry; if (this->entries - 1 - this->first_on_screen < this->lines - 2) this->current_entry = this->entries - 1; else if (this->entries - 1 - this->current_entry < this->lines - 2) { this->current_entry = this->entries - 1; this->first_on_screen = this->entries - 1 - (this->lines - 2) + 1; } else { this->current_entry += this->lines - 2; this->first_on_screen = min(this->first_on_screen + this->lines - 2, (this->entries - 1) - (this->lines - 2) + 1); } if (this->entries > this->lines - 2) panel_update(this); else { panel_update_entry(this, old_current_entry); panel_update_entry(this, this->current_entry); } break; case act_HOME: if (this->current_entry == 0) break; this->current_entry = this->first_on_screen = 0; panel_update(this); break; case act_END: if (this->current_entry == this->entries - 1) break; this->current_entry = this->entries - 1; this->first_on_screen = max(0, (this->entries - 1) - (this->lines - 2) + 1); panel_update(this); break; case act_CHDIR: this->first_on_screen = this->current_entry = 0; strcpy(this->path, (char *)aux_info); panel_action(this, act_REFRESH, NULL, NULL, 1); break; case act_MODE: this->mode = (this->mode + 1) % FILE_DISPLAY_MODES; panel_update(this); break; case act_SWITCH: xchg(&this->lines, &link->lines); xchg(&this->columns, &link->columns); xchg(&this->begin_x, &link->begin_x); xchg(&this->begin_y, &link->begin_y); free(this->win); this->win = window_init(this->begin_x, this->begin_y, this->lines, this->columns); free(link->win); link->win = window_init(link->begin_x, link->begin_y, link->lines, link->columns); break; case act_REFRESH: if (this->dir_entry[this->current_entry].name) strcpy(oldentry, this->dir_entry[this->current_entry].name); if (!panel_getdirinfo(this, this->path, verify)) panel_recover(this); else if (verify) { this->current_entry = min(panel_getindex(this, oldentry), this->entries - 1); this->first_on_screen = max(0, this->current_entry - (this->lines - 2) + 1); } else this->current_entry = this->first_on_screen = 0; panel_update_frame(this); panel_update_path(this); panel_update_info(this); panel_update_size(this); panel_update(this); break; default: fatal("no action"); break; } if (done != -1) panel_update_info(this); return done; }