/* * Copyright 1989 Software Research Associates, Inc., Tokyo, Japan * * Permission to use, copy, modify, and distribute this software and its * documentation for any purpose and without fee is hereby granted, provided * that the above copyright notice appear in all copies and that both that * copyright notice and this permission notice appear in supporting * documentation, and that the name of Software Research Associates not be used * in advertising or publicity pertaining to distribution of the software * without specific, written prior permission. Software Research Associates * makes no representations about the suitability of this software for any * purpose. It is provided "as is" without express or implied warranty. * * SOFTWARE RESEARCH ASSOCIATES DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS * SOFTWARE, INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, * IN NO EVENT SHALL SOFTWARE RESEARCH ASSOCIATES BE LIABLE FOR ANY SPECIAL, * INDIRECT OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR * PERFORMANCE OF THIS SOFTWARE. * * Author: Erik M. van der Poel * Software Research Associates, Inc., Tokyo, Japan * erik@sra.co.jp */ #include #ifdef SEL_FILE_IGNORE_CASE #include #endif /* def SEL_FILE_IGNORE_CASE */ #include #include #include "SFinternal.h" #include "xstat.h" #include #if defined(SVR4) || defined(SYSV) || defined(USG) extern uid_t getuid(); extern void qsort(); #endif /* defined(SVR4) || defined(SYSV) || defined(USG) */ typedef struct { char *name; char *dir; } SFLogin; SFDir *SFdirs = NULL; int SFdirEnd; int SFdirPtr; int SFbuttonPressed = 0; static int SFdoNotTouchDirPtr = 0; static int SFdoNotTouchVorigin = 0; static SFDir SFrootDir, SFhomeDir; static SFLogin *SFlogins; static int SFtwiddle = 0; int SFchdir(path) char *path; { int result; result = 0; if (strcmp(path, SFcurrentDir)) { result = chdir(path); if (!result) { (void) strcpy(SFcurrentDir, path); } } return result; } static SFfree(i) int i; { register SFDir *dir; register int j; dir = &(SFdirs[i]); for (j = dir->nEntries - 1; j >= 0; j--) { if (dir->entries[j].shown != dir->entries[j].real) { XtFree(dir->entries[j].shown); } XtFree(dir->entries[j].real); } XtFree((char *) dir->entries); XtFree(dir->dir); dir->dir = NULL; } static SFstrdup(s1, s2) char **s1; char *s2; { *s1 = strcpy(XtMalloc((unsigned) (strlen(s2) + 1)), s2); } static SFunreadableDir(dir) SFDir *dir; { char *cannotOpen = " "; dir->entries = (SFEntry *) XtMalloc(sizeof(SFEntry)); dir->entries[0].statDone = 1; SFstrdup(&dir->entries[0].real, cannotOpen); dir->entries[0].shown = dir->entries[0].real; dir->nEntries = 1; dir->nChars = strlen(cannotOpen); } #ifdef SEL_FILE_IGNORE_CASE static SFstrncmp(p, q, n) register char *p, *q; register int n; { register char c1, c2; char *psave, *qsave; int nsave; psave = p; qsave = q; nsave = n; c1 = *p++; if (islower(c1)) { c1 = toupper(c1); } c2 = *q++; if (islower(c2)) { c2 = toupper(c2); } while ((--n >= 0) && (c1 == c2)) { if (!c1) { return strncmp(psave, qsave, nsave); } c1 = *p++; if (islower(c1)) { c1 = toupper(c1); } c2 = *q++; if (islower(c2)) { c2 = toupper(c2); } } if (n < 0) { return strncmp(psave, qsave, nsave); } return c1 - c2; } #endif /* def SEL_FILE_IGNORE_CASE */ static SFreplaceText(dir, str) SFDir *dir; char *str; { int len; *(dir->path) = 0; len = strlen(str); if (str[len - 1] == '/') { (void) strcat(SFcurrentPath, str); } else { (void) strncat(SFcurrentPath, str, len - 1); } if (strncmp(SFcurrentPath, SFstartDir, strlen(SFstartDir))) { SFsetText(SFcurrentPath); } else { SFsetText(&(SFcurrentPath[strlen(SFstartDir)])); } SFtextChanged(); } static void SFexpand(str) char *str; { int len; int cmp; char *name, *growing; SFDir *dir; SFEntry *entry, *max; len = strlen(str); dir = &(SFdirs[SFdirEnd - 1]); if (dir->beginSelection == -1) { SFstrdup(&str, str); SFreplaceText(dir, str); XtFree(str); return; } else if (dir->beginSelection == dir->endSelection) { SFreplaceText(dir, dir->entries[dir->beginSelection].shown); return; } max = &(dir->entries[dir->endSelection + 1]); name = dir->entries[dir->beginSelection].shown; SFstrdup(&growing, name); cmp = 0; while (!cmp) { entry = &(dir->entries[dir->beginSelection]); while (entry < max) { if (cmp = strncmp(growing, entry->shown, len)) { break; } entry++; } len++; } /* * SFreplaceText() expects filename */ growing[len - 2] = ' '; growing[len - 1] = 0; SFreplaceText(dir, growing); XtFree(growing); } static int SFfindFile(dir, str) SFDir *dir; register char *str; { register int i, last, max; register char *name, save; SFEntry *entries; int len; int begin, end; int result; len = strlen(str); if (str[len - 1] == ' ') { SFexpand(str); return 1; } else if (str[len - 1] == '/') { len--; } max = dir->nEntries; entries = dir->entries; i = 0; while (i < max) { name = entries[i].shown; last = strlen(name) - 1; save = name[last]; name[last] = 0; #ifdef SEL_FILE_IGNORE_CASE result = SFstrncmp(str, name, len); #else /* def SEL_FILE_IGNORE_CASE */ result = strncmp(str, name, len); #endif /* def SEL_FILE_IGNORE_CASE */ name[last] = save; if (result <= 0) { break; } i++; } begin = i; while (i < max) { name = entries[i].shown; last = strlen(name) - 1; save = name[last]; name[last] = 0; #ifdef SEL_FILE_IGNORE_CASE result = SFstrncmp(str, name, len); #else /* def SEL_FILE_IGNORE_CASE */ result = strncmp(str, name, len); #endif /* def SEL_FILE_IGNORE_CASE */ name[last] = save; if (result) { break; } i++; } end = i; if (begin != end) { if ( (dir->beginSelection != begin) || (dir->endSelection != end - 1) ) { dir->changed = 1; dir->beginSelection = begin; if (str[strlen(str) - 1] == '/') { dir->endSelection = begin; } else { dir->endSelection = end - 1; } } } else { if (dir->beginSelection != -1) { dir->changed = 1; dir->beginSelection = -1; dir->endSelection = -1; } } if ( SFdoNotTouchVorigin || ((begin > dir->vOrigin) && (end < dir->vOrigin + SFlistSize)) ) { SFdoNotTouchVorigin = 0; return 0; } i = begin - 1; if (i > max - SFlistSize) { i = max - SFlistSize; } if (i < 0) { i = 0; } if (dir->vOrigin != i) { dir->vOrigin = i; dir->changed = 1; } return 0; } static SFunselect() { SFDir *dir; dir = &(SFdirs[SFdirEnd - 1]); if (dir->beginSelection != -1) { dir->changed = 1; } dir->beginSelection = -1; dir->endSelection = -1; } static int SFcompareLogins(p, q) SFLogin *p, *q; { return strcmp(p->name, q->name); } static SFgetHomeDirs() { struct passwd *pw; int alloc; int i; SFEntry *entries = NULL; int len; int maxChars; { alloc = 1; i = 1; entries = (SFEntry *) XtMalloc(sizeof(SFEntry)); SFlogins = (SFLogin *) XtMalloc(sizeof(SFLogin)); entries[0].real = XtMalloc(3); (void) strcpy(entries[0].real, "~"); entries[0].shown = entries[0].real; entries[0].statDone = 1; SFlogins[0].name = ""; pw = getpwuid((int) getuid()); SFstrdup(&SFlogins[0].dir, pw ? pw->pw_dir : "/"); maxChars = 0; } (void) setpwent(); while ((pw = getpwent()) && (*(pw->pw_name))) { if (i >= alloc) { alloc *= 2; entries = (SFEntry *) XtRealloc( (char *) entries, (unsigned) (alloc * sizeof(SFEntry)) ); SFlogins = (SFLogin *) XtRealloc( (char *) SFlogins, (unsigned) (alloc * sizeof(SFLogin)) ); } len = strlen(pw->pw_name); entries[i].real = XtMalloc((unsigned) (len + 3)); (void) strcat(strcpy(entries[i].real, "~"), pw->pw_name); entries[i].shown = entries[i].real; entries[i].statDone = 1; if (len > maxChars) { maxChars = len; } SFstrdup(&SFlogins[i].name, pw->pw_name); SFstrdup(&SFlogins[i].dir, pw->pw_dir); i++; } SFhomeDir.dir = XtMalloc(1) ; SFhomeDir.dir[0] = 0 ; SFhomeDir.path = SFcurrentPath ; SFhomeDir.entries = entries ; SFhomeDir.nEntries = i ; SFhomeDir.vOrigin = 0 ; /* :-) */ SFhomeDir.nChars = maxChars + 2 ; SFhomeDir.hOrigin = 0 ; SFhomeDir.changed = 1 ; SFhomeDir.beginSelection = -1 ; SFhomeDir.endSelection = -1 ; #if defined(SVR4) || defined(SYSV) || defined(USG) qsort((char *) entries, (unsigned)i, sizeof(SFEntry), SFcompareEntries); qsort((char *) SFlogins, (unsigned)i, sizeof(SFLogin), SFcompareLogins); #else /* defined(SVR4) || defined(SYSV) || defined(USG) */ qsort((char *) entries, i, sizeof(SFEntry), SFcompareEntries); qsort((char *) SFlogins, i, sizeof(SFLogin), SFcompareLogins); #endif /* defined(SVR4) || defined(SYSV) || defined(USG) */ for (i--; i >= 0; i--) { (void) strcat(entries[i].real, "/"); } } static int SFfindHomeDir(begin, end) char *begin, *end; { char save; char *theRest; int i; save = *end; *end = 0; for (i = SFhomeDir.nEntries - 1; i >= 0; i--) { if (!strcmp(SFhomeDir.entries[i].real, begin)) { *end = save; SFstrdup(&theRest, end); (void) strcat(strcat(strcpy(SFcurrentPath, SFlogins[i].dir), "/"), theRest); XtFree(theRest); SFsetText(SFcurrentPath); SFtextChanged(); return 1; } } *end = save; return 0; } SFupdatePath() { static int alloc; static int wasTwiddle = 0; char *begin, *end; int i, j; int prevChange; int SFdirPtrSave, SFdirEndSave; SFDir *dir; if (!SFdirs) { SFdirs = (SFDir *) XtMalloc((alloc = 10) * sizeof(SFDir)); dir = &(SFdirs[0]); SFstrdup(&dir->dir, "/"); (void) SFchdir("/"); (void) SFgetDir(dir); for (j = 1; j < alloc; j++) { SFdirs[j].dir = NULL; } dir->path = SFcurrentPath + 1; dir->vOrigin = 0; dir->hOrigin = 0; dir->changed = 1; dir->beginSelection = -1; dir->endSelection = -1; SFhomeDir.dir = NULL; } SFdirEndSave = SFdirEnd; SFdirEnd = 1; SFdirPtrSave = SFdirPtr; SFdirPtr = 0; begin = NULL; if (SFcurrentPath[0] == '~') { if (!SFtwiddle) { SFtwiddle = 1; dir = &(SFdirs[0]); SFrootDir = *dir; if (!SFhomeDir.dir) { SFgetHomeDirs(); } *dir = SFhomeDir; dir->changed = 1; } end = SFcurrentPath; SFdoNotTouchDirPtr = 1; wasTwiddle = 1; } else { if (SFtwiddle) { SFtwiddle = 0; dir = &(SFdirs[0]); *dir = SFrootDir; dir->changed = 1; } end = SFcurrentPath + 1; } i = 0; prevChange = 0; while (*end) { while (*end++ == '/') { ; } end--; begin = end; while ((*end) && (*end++ != '/')) { ; } if ((end - SFcurrentPath <= SFtextPos) && (*(end - 1) == '/')) { SFdirPtr = i - 1; if (SFdirPtr < 0) { SFdirPtr = 0; } } if (*begin) { if (*(end - 1) == '/') { char save = *end; if (SFtwiddle) { if (SFfindHomeDir(begin, end)) { return; } } *end = 0; i++; SFdirEnd++; if (i >= alloc) { SFdirs = (SFDir *) XtRealloc( (char *) SFdirs, (unsigned) ((alloc *= 2) * sizeof(SFDir)) ); for (j = alloc / 2; j < alloc; j++) { SFdirs[j].dir = NULL; } } dir = &(SFdirs[i]); if ( (!(dir->dir)) || prevChange || strcmp(dir->dir, begin) ) { if (dir->dir) { SFfree(i); } prevChange = 1; SFstrdup(&dir->dir, begin); dir->path = end; dir->vOrigin = 0; dir->hOrigin = 0; dir->changed = 1; dir->beginSelection = -1; dir->endSelection = -1; (void) SFfindFile(dir - 1, begin); if ( SFchdir(SFcurrentPath) || SFgetDir(dir) ) { SFunreadableDir(dir); break; } } *end = save; if (!save) { SFunselect(); } } else { if (SFfindFile(&(SFdirs[SFdirEnd-1]), begin)) { return; } } } else { SFunselect(); } } if ((end == SFcurrentPath + 1) && (!SFtwiddle)) { SFunselect(); } for (i = SFdirEnd; i < alloc; i++) { if (SFdirs[i].dir) { SFfree(i); } } if (SFdoNotTouchDirPtr) { if (wasTwiddle) { wasTwiddle = 0; SFdirPtr = SFdirEnd - 2; if (SFdirPtr < 0) { SFdirPtr = 0; } } else { SFdirPtr = SFdirPtrSave; } SFdoNotTouchDirPtr = 0; } if ((SFdirPtr != SFdirPtrSave) || (SFdirEnd != SFdirEndSave)) { XawScrollbarSetThumb( selFileHScroll, (float) (((double) SFdirPtr) / SFdirEnd), (float) (((double) ((SFdirEnd < 3) ? SFdirEnd : 3)) / SFdirEnd) ); } if (SFdirPtr != SFdirPtrSave) { SFdrawLists(SF_DO_SCROLL); } else { for (i = 0; i < 3; i++) { if (SFdirPtr + i < SFdirEnd) { if (SFdirs[SFdirPtr + i].changed) { SFdirs[SFdirPtr + i].changed = 0; SFdrawList(i, SF_DO_SCROLL); } } else { SFclearList(i, SF_DO_SCROLL); } } } } SFsetText(path) char *path; { XawTextBlock text; text.firstPos = 0; text.length = strlen(path); text.ptr = path; text.format = FMT8BIT; XawTextReplace(selFileField, 0, strlen(SFtextBuffer), &text); XawTextSetInsertionPoint(selFileField, strlen(SFtextBuffer)); } /* ARGSUSED */ void SFbuttonPressList(w, n, event) Widget w; int n; XButtonPressedEvent *event; { SFbuttonPressed = 1; } /* ARGSUSED */ void SFbuttonReleaseList(w, n, event) Widget w; int n; XButtonReleasedEvent *event; { SFDir *dir; SFbuttonPressed = 0; if (SFcurrentInvert[n] != -1) { if (n < 2) { SFdoNotTouchDirPtr = 1; } SFdoNotTouchVorigin = 1; dir = &(SFdirs[SFdirPtr + n]); SFreplaceText( dir, dir->entries[dir->vOrigin + SFcurrentInvert[n]].shown ); SFmotionList(w, n, event); } } static int SFcheckDir(n, dir) int n; SFDir *dir; { struct stat statBuf; int i; if ( (!stat(".", &statBuf)) && (statBuf.st_mtime != dir->mtime) ) { /* * If the pointer is currently in the window that we are about * to update, we must warp it to prevent the user from * accidentally selecting the wrong file. */ if (SFcurrentInvert[n] != -1) { XWarpPointer( SFdisplay, None, XtWindow(selFileLists[n]), 0, 0, 0, 0, 0, 0 ); } for (i = dir->nEntries - 1; i >= 0; i--) { if (dir->entries[i].shown != dir->entries[i].real) { XtFree(dir->entries[i].shown); } XtFree(dir->entries[i].real); } XtFree((char *) dir->entries); if (SFgetDir(dir)) { SFunreadableDir(dir); } if (dir->vOrigin > dir->nEntries - SFlistSize) { dir->vOrigin = dir->nEntries - SFlistSize; } if (dir->vOrigin < 0) { dir->vOrigin = 0; } if (dir->hOrigin > dir->nChars - SFcharsPerEntry) { dir->hOrigin = dir->nChars - SFcharsPerEntry; } if (dir->hOrigin < 0) { dir->hOrigin = 0; } dir->beginSelection = -1; dir->endSelection = -1; SFdoNotTouchVorigin = 1; if ((dir + 1)->dir) { (void) SFfindFile(dir, (dir + 1)->dir); } else { (void) SFfindFile(dir, dir->path); } if (!SFworkProcAdded) { (void) XtAppAddWorkProc(SFapp, SFworkProc, NULL); SFworkProcAdded = 1; } return 1; } return 0; } static int SFcheckFiles(dir) SFDir *dir; { int from, to; int result; char old, new; int i; char *str; int last; struct stat statBuf; result = 0; from = dir->vOrigin; to = dir->vOrigin + SFlistSize; if (to > dir->nEntries) { to = dir->nEntries; } for (i = from; i < to; i++) { str = dir->entries[i].real; last = strlen(str) - 1; old = str[last]; str[last] = 0; if (stat(str, &statBuf)) { new = ' '; } else { new = SFstatChar(&statBuf); } str[last] = new; if (new != old) { result = 1; } } return result; } void SFdirModTimer(cl, id) XtPointer cl; XtIntervalId *id; { static int n = -1; static int f = 0; char save; SFDir *dir; if ((!SFtwiddle) && (SFdirPtr < SFdirEnd)) { n++; if ((n > 2) || (SFdirPtr + n >= SFdirEnd)) { n = 0; f++; if ((f > 2) || (SFdirPtr + f >= SFdirEnd)) { f = 0; } } dir = &(SFdirs[SFdirPtr + n]); save = *(dir->path); *(dir->path) = 0; if (SFchdir(SFcurrentPath)) { *(dir->path) = save; /* * force a re-read */ *(dir->dir) = 0; SFupdatePath(); } else { *(dir->path) = save; if ( SFcheckDir(n, dir) || ((f == n) && SFcheckFiles(dir)) ) { SFdrawList(n, SF_DO_SCROLL); } } } SFdirModTimerId = XtAppAddTimeOut(SFapp, (unsigned long) 1000, SFdirModTimer, (XtPointer) NULL); } /* Return a single character describing what kind of file STATBUF is. */ char SFstatChar (statBuf) struct stat *statBuf; { if (S_ISDIR (statBuf->st_mode)) { return '/'; } else if (S_ISREG (statBuf->st_mode)) { return S_ISXXX (statBuf->st_mode) ? '*' : ' '; #ifdef S_ISSOCK } else if (S_ISSOCK (statBuf->st_mode)) { return '='; #endif /* S_ISSOCK */ } else { return ' '; } }