#define TRACE #include /* * $Author: tom $ * $Date: 2016/11/20 20:12:18 $ * $Revision: 1.85 $ */ /* * Declare file local prototypes. */ /* *INDENT-OFF* */ static BINDFN_PROTO (completeFilenameCB); static BINDFN_PROTO (displayFileInfoCB); static BINDFN_PROTO (fselectAdjustScrollCB); static char *contentToPath (CDKFSELECT *fselect, char *content); static char *errorMessage (const char *format); static char *expandTilde (const char *filename); static char *format1Date (const char *format, time_t value); static char *format1Number (const char *format, long value); static char *format1String (const char *format, const char *string); static char *format3String (const char *format, const char *s1, const char *s2, const char *s3); static char *format1StrVal (const char *format, const char *string, int value); static char *trim1Char (char *source); static int createList (CDKFSELECT *widget, CDK_CSTRING2 list, int listSize); static void setPWD (CDKFSELECT *fselect); /* *INDENT-ON* */ DeclareSetXXchar (static, _setMy); DeclareCDKObjects (FSELECT, Fselect, _setMy, String); /* * This creates a file selection widget. */ CDKFSELECT *newCDKFselect (CDKSCREEN *cdkscreen, int xplace, int yplace, int height, int width, const char *title, const char *label, chtype fieldAttribute, chtype fillerChar, chtype highlight, const char *dAttribute, const char *fAttribute, const char *lAttribute, const char *sAttribute, boolean Box, boolean shadow) { /* *INDENT-EQLS* */ CDKFSELECT *fselect = 0; int parentWidth = getmaxx (cdkscreen->window); int parentHeight = getmaxy (cdkscreen->window); int boxWidth; int boxHeight; int xpos = xplace; int ypos = yplace; int tempWidth = 0; int tempHeight = 0; int labelLen, junk; chtype *chtypeString; int x; /* *INDENT-OFF* */ static const struct { int from; int to; } bindings[] = { { CDK_BACKCHAR, KEY_PPAGE }, { CDK_FORCHAR, KEY_NPAGE }, }; /* *INDENT-ON* */ if ((fselect = newCDKObject (CDKFSELECT, &my_funcs)) == 0) return (0); setCDKFselectBox (fselect, Box); /* * If the height is a negative value, the height will * be ROWS-height, otherwise, the height will be the * given height. */ boxHeight = setWidgetDimension (parentHeight, height, 0); /* * If the width is a negative value, the width will * be COLS-width, otherwise, the width will be the * given width. */ boxWidth = setWidgetDimension (parentWidth, width, 0); /* Rejustify the x and y positions if we need to. */ alignxy (cdkscreen->window, &xpos, &ypos, boxWidth, boxHeight); /* Make sure the box isn't too small. */ boxWidth = (boxWidth < 15 ? 15 : boxWidth); boxHeight = (boxHeight < 6 ? 6 : boxHeight); /* Make the file selector window. */ fselect->win = newwin (boxHeight, boxWidth, ypos, xpos); /* Is the window null? */ if (fselect->win == 0) { destroyCDKObject (fselect); return (0); } keypad (fselect->win, TRUE); /* *INDENT-EQLS* Set some variables. */ ScreenOf (fselect) = cdkscreen; fselect->parent = cdkscreen->window; fselect->dirAttribute = copyChar (dAttribute); fselect->fileAttribute = copyChar (fAttribute); fselect->linkAttribute = copyChar (lAttribute); fselect->sockAttribute = copyChar (sAttribute); fselect->highlight = highlight; fselect->fillerCharacter = fillerChar; fselect->fieldAttribute = fieldAttribute; fselect->boxHeight = boxHeight; fselect->boxWidth = boxWidth; fselect->fileCounter = 0; fselect->pwd = 0; initExitType (fselect); ObjOf (fselect)->inputWindow = fselect->win; fselect->shadow = shadow; fselect->shadowWin = 0; /* Get the present working directory. */ setPWD (fselect); /* Get the contents of the current directory. */ setCDKFselectDirContents (fselect); /* Create the entry field in the selector. */ chtypeString = char2Chtype (label, &labelLen, &junk); freeChtype (chtypeString); tempWidth = (isFullWidth (width) ? FULL : boxWidth - 2 - labelLen); fselect->entryField = newCDKEntry (cdkscreen, getbegx (fselect->win), getbegy (fselect->win), title, label, fieldAttribute, fillerChar, vMIXED, tempWidth, 0, 512, Box, FALSE); /* Make sure the widget was created. */ if (fselect->entryField == 0) { destroyCDKObject (fselect); return (0); } /* Set the lower left/right characters of the entry field. */ setCDKEntryLLChar (fselect->entryField, ACS_LTEE); setCDKEntryLRChar (fselect->entryField, ACS_RTEE); /* Define the callbacks for the entry field. */ bindCDKObject (vENTRY, fselect->entryField, KEY_UP, fselectAdjustScrollCB, fselect); bindCDKObject (vENTRY, fselect->entryField, KEY_PPAGE, fselectAdjustScrollCB, fselect); bindCDKObject (vENTRY, fselect->entryField, KEY_DOWN, fselectAdjustScrollCB, fselect); bindCDKObject (vENTRY, fselect->entryField, KEY_NPAGE, fselectAdjustScrollCB, fselect); bindCDKObject (vENTRY, fselect->entryField, KEY_TAB, completeFilenameCB, fselect); bindCDKObject (vENTRY, fselect->entryField, CTRL ('^'), displayFileInfoCB, fselect); /* Put the current working directory in the entry field. */ setCDKEntryValue (fselect->entryField, fselect->pwd); /* Create the scrolling list in the selector. */ tempHeight = getmaxy (fselect->entryField->win) - BorderOf (fselect); tempWidth = (isFullWidth (width) ? FULL : boxWidth - 1); fselect->scrollField = newCDKScroll (cdkscreen, getbegx (fselect->win), getbegy (fselect->win) + tempHeight, RIGHT, boxHeight - tempHeight, tempWidth, 0, (CDK_CSTRING2)fselect->dirContents, fselect->fileCounter, NONUMBERS, fselect->highlight, Box, FALSE); /* Set the lower left/right characters of the entry field. */ setCDKScrollULChar (fselect->scrollField, ACS_LTEE); setCDKScrollURChar (fselect->scrollField, ACS_RTEE); /* Do we want a shadow? */ if (shadow) { fselect->shadowWin = newwin (boxHeight, boxWidth, ypos + 1, xpos + 1); } /* Setup the key bindings. */ for (x = 0; x < (int)SIZEOF (bindings); ++x) bindCDKObject (vFSELECT, fselect, (chtype)bindings[x].from, getcCDKBind, (void *)(long)bindings[x].to); registerCDKObject (cdkscreen, vFSELECT, fselect); return (fselect); } /* * This erases the file selector from the screen. */ static void _eraseCDKFselect (CDKOBJS *object) { if (validCDKObject (object)) { CDKFSELECT *fselect = (CDKFSELECT *)object; eraseCDKScroll (fselect->scrollField); eraseCDKEntry (fselect->entryField); eraseCursesWindow (fselect->win); } } /* * This moves the fselect field to the given location. */ static void _moveCDKFselect (CDKOBJS *object, int xplace, int yplace, boolean relative, boolean refresh_flag) { CDKFSELECT *fselect = (CDKFSELECT *)object; /* *INDENT-EQLS* */ int currentX = getbegx (fselect->win); int currentY = getbegy (fselect->win); int xpos = xplace; int ypos = yplace; int xdiff = 0; int ydiff = 0; /* * If this is a relative move, then we will adjust where we want * to move to. */ if (relative) { xpos = getbegx (fselect->win) + xplace; ypos = getbegy (fselect->win) + yplace; } /* Adjust the window if we need to. */ alignxy (WindowOf (fselect), &xpos, &ypos, fselect->boxWidth, fselect->boxHeight); /* Get the difference. */ xdiff = currentX - xpos; ydiff = currentY - ypos; /* Move the window to the new location. */ moveCursesWindow (fselect->win, -xdiff, -ydiff); moveCursesWindow (fselect->shadowWin, -xdiff, -ydiff); /* Move the sub-widgets. */ moveCDKEntry (fselect->entryField, xplace, yplace, relative, FALSE); moveCDKScroll (fselect->scrollField, xplace, yplace, relative, FALSE); /* Redraw the window, if they asked for it. */ if (refresh_flag) { drawCDKFselect (fselect, ObjOf (fselect)->box); } } /* * The fselect's focus resides in the entry widget. But the scroll widget * will not draw items highlighted unless it has focus. Temporarily adjust the * focus of the scroll widget when drawing on it to get the right highlighting. */ #define SaveFocus(widget) \ boolean save = HasFocusObj (ObjOf (widget->scrollField)); \ HasFocusObj (ObjOf (widget->scrollField)) = \ HasFocusObj (ObjOf (widget->entryField)) #define RestoreFocus(widget) \ HasFocusObj (ObjOf (widget->scrollField)) = save static void drawMyScroller (CDKFSELECT *widget) { SaveFocus (widget); drawCDKScroll (widget->scrollField, ObjOf (widget->scrollField)->box); RestoreFocus (widget); } static void injectMyScroller (CDKFSELECT *widget, chtype key) { SaveFocus (widget); (void)injectCDKScroll (widget->scrollField, key); RestoreFocus (widget); } /* * This draws the file selector widget. */ static void _drawCDKFselect (CDKOBJS *object, boolean Box GCC_UNUSED) { CDKFSELECT *fselect = (CDKFSELECT *)object; /* Draw in the shadow if we need to. */ if (fselect->shadowWin != 0) { drawShadow (fselect->shadowWin); } /* Draw in the entry field. */ drawCDKEntry (fselect->entryField, ObjOf (fselect->entryField)->box); /* Draw in the scroll field. */ drawMyScroller (fselect); } /* * This means you want to use the given file selector. It takes input * from the keyboard, and when it's done, it fills the entry info * element of the structure with what was typed. */ char *activateCDKFselect (CDKFSELECT *fselect, chtype *actions) { chtype input = 0; boolean functionKey; char *ret = 0; /* Draw the widget. */ drawCDKFselect (fselect, ObjOf (fselect)->box); if (actions == 0) { for (;;) { input = (chtype)getchCDKObject (ObjOf (fselect->entryField), &functionKey); /* Inject the character into the widget. */ ret = injectCDKFselect (fselect, input); if (fselect->exitType != vEARLY_EXIT) { return ret; } } } else { int length = chlen (actions); int x = 0; /* Inject each character one at a time. */ for (x = 0; x < length; x++) { ret = injectCDKFselect (fselect, actions[x]); if (fselect->exitType != vEARLY_EXIT) { return ret; } } } /* Set the exit type and exit. */ setExitType (fselect, 0); return 0; } /* * This injects a single character into the file selector. */ static int _injectCDKFselect (CDKOBJS *object, chtype input) { CDKFSELECT *fselect = (CDKFSELECT *)object; char *filename; boolean file; char *ret = unknownString; bool complete = FALSE; /* Let the user play. */ filename = injectCDKEntry (fselect->entryField, input); /* Copy the entry field exitType to the fileselector. */ copyExitType (fselect, fselect->entryField); /* If we exited early, make sure we don't interpret it as a file. */ if (fselect->exitType == vEARLY_EXIT) { return 0; } /* Can we change into the directory? */ file = chdir (filename); if (chdir (fselect->pwd) != 0) { return 0; } /* If it's not a directory, return the filename. */ if (file != 0) { /* It's a regular file, create the full path. */ fselect->pathname = copyChar (filename); /* Return the complete pathname. */ ret = (fselect->pathname); complete = TRUE; } else { /* Set the file selector information. */ setCDKFselect (fselect, filename, fselect->fieldAttribute, fselect->fillerCharacter, fselect->highlight, fselect->dirAttribute, fselect->fileAttribute, fselect->linkAttribute, fselect->sockAttribute, ObjOf (fselect)->box); /* Redraw the scrolling list. */ drawMyScroller (fselect); } if (!complete) setExitType (fselect, 0); ResultOf (fselect).valueString = ret; return (ret != unknownString); } /* * This function sets the information inside the file selector. */ void setCDKFselect (CDKFSELECT *fselect, const char *directory, chtype fieldAttrib, chtype filler, chtype highlight, const char *dirAttribute, const char *fileAttribute, const char *linkAttribute, const char *sockAttribute, boolean Box GCC_UNUSED) { /* *INDENT-EQLS* */ CDKSCROLL *fscroll = fselect->scrollField; CDKENTRY *fentry = fselect->entryField; char *tempDir = 0; /* Keep the info sent to us. */ fselect->fieldAttribute = fieldAttrib; fselect->fillerCharacter = filler; fselect->highlight = highlight; /* Set the attributes of the entry field/scrolling list. */ setCDKEntryFillerChar (fentry, filler); setCDKScrollHighlight (fscroll, highlight); /* Only do the directory stuff if the directory is not null. */ if (directory != 0) { char *newDirectory; /* Try to expand the directory if it starts with a ~ */ if ((tempDir = expandTilde (directory)) != 0) { newDirectory = tempDir; } else { newDirectory = copyChar (directory); } /* Change directories. */ if (chdir (newDirectory) != 0) { char *mesg[10]; Beep (); /* Could not get into the directory, pop up a little message. */ mesg[0] = format1String ("Could not change into %s", newDirectory); mesg[1] = errorMessage ("%s"); mesg[2] = copyChar (" "); mesg[3] = copyChar ("Press Any Key To Continue."); /* Pop Up a message. */ popupLabel (ScreenOf (fselect), (CDK_CSTRING2)mesg, 4); /* Clean up some memory. */ freeCharList (mesg, 4); /* Get out of here. */ eraseCDKFselect (fselect); drawCDKFselect (fselect, ObjOf (fselect)->box); freeChar (newDirectory); return; } freeChar (newDirectory); } /* * If the information coming in is the same as the information * that is already there, there is no need to destroy it. */ if (fselect->pwd != directory) { setPWD (fselect); } if (fselect->fileAttribute != fileAttribute) { /* Remove the old pointer and set the new value. */ freeChar (fselect->fileAttribute); fselect->fileAttribute = copyChar (fileAttribute); } if (fselect->dirAttribute != dirAttribute) { /* Remove the old pointer and set the new value. */ freeChar (fselect->dirAttribute); fselect->dirAttribute = copyChar (dirAttribute); } if (fselect->linkAttribute != linkAttribute) { /* Remove the old pointer and set the new value. */ freeChar (fselect->linkAttribute); fselect->linkAttribute = copyChar (linkAttribute); } if (fselect->sockAttribute != sockAttribute) { /* Remove the old pointer and set the new value. */ freeChar (fselect->sockAttribute); fselect->sockAttribute = copyChar (sockAttribute); } /* Set the contents of the entry field. */ setCDKEntryValue (fentry, fselect->pwd); drawCDKEntry (fentry, ObjOf (fentry)->box); /* Get the directory contents. */ if (setCDKFselectDirContents (fselect) == 0) { Beep (); return; } /* Set the values in the scrolling list. */ setCDKScrollItems (fscroll, (CDK_CSTRING2)fselect->dirContents, fselect->fileCounter, FALSE); } /* * This creates a list of the files in the current directory. */ int setCDKFselectDirContents (CDKFSELECT *fselect) { struct stat fileStat; char **dirList = 0; int fileCount; int x = 0; /* Get the directory contents. */ fileCount = CDKgetDirectoryContents (fselect->pwd, &dirList); if (fileCount <= 0) { /* We couldn't read the directory. Return. */ CDKfreeStrings (dirList); return 0; } /* Clean out the old directory list. */ CDKfreeStrings (fselect->dirContents); fselect->dirContents = dirList; fselect->fileCounter = fileCount; /* Set the properties of the files. */ for (x = 0; x < fselect->fileCounter; x++) { char *oldItem; const char *attr = ""; const char *mode = "?"; /* FIXME: access() would give a more correct answer */ if (lstat (dirList[x], &fileStat) == 0) { mode = " "; if ((fileStat.st_mode & S_IXUSR) != 0) { mode = "*"; } #if defined (S_IXGRP) && defined (S_IXOTH) else if (((fileStat.st_mode & S_IXGRP) != 0) || ((fileStat.st_mode & S_IXOTH) != 0)) { mode = "*"; } #endif } switch (mode2Filetype (fileStat.st_mode)) { case 'l': attr = fselect->linkAttribute; mode = "@"; break; case '@': attr = fselect->sockAttribute; mode = "&"; break; case '-': attr = fselect->fileAttribute; break; case 'd': attr = fselect->dirAttribute; mode = "/"; break; default: break; } oldItem = dirList[x]; fselect->dirContents[x] = format3String ("%s%s%s", attr, dirList[x], mode); free (oldItem); } return 1; } char **getCDKFselectDirContents (CDKFSELECT *fselect, int *count) { (*count) = fselect->fileCounter; return fselect->dirContents; } /* * This sets the current directory of the file selector. */ int setCDKFselectDirectory (CDKFSELECT *fselect, const char *directory) { /* *INDENT-EQLS* */ CDKENTRY *fentry = fselect->entryField; CDKSCROLL *fscroll = fselect->scrollField; int result = 1; /* * If the directory supplied is the same as what is already * there, return. */ if (fselect->pwd != directory) { /* Try to chdir into the given directory. */ if (chdir (directory) != 0) { result = 0; } else { setPWD (fselect); /* Set the contents of the entry field. */ setCDKEntryValue (fentry, fselect->pwd); drawCDKEntry (fentry, ObjOf (fentry)->box); /* Get the directory contents. */ if (setCDKFselectDirContents (fselect) == 0) { result = 0; } else { /* Set the values in the scrolling list. */ setCDKScrollItems (fscroll, (CDK_CSTRING2)fselect->dirContents, fselect->fileCounter, FALSE); } } } return result; } char *getCDKFselectDirectory (CDKFSELECT *fselect) { return fselect->pwd; } /* * This sets the filler character of the entry field. */ void setCDKFselectFillerChar (CDKFSELECT *fselect, chtype filler) { CDKENTRY *fentry = fselect->entryField; fselect->fillerCharacter = filler; setCDKEntryFillerChar (fentry, filler); } chtype getCDKFselectFillerChar (CDKFSELECT *fselect) { return fselect->fillerCharacter; } /* * This sets the highlight bar of the scrolling list. */ void setCDKFselectHighlight (CDKFSELECT *fselect, chtype highlight) { CDKSCROLL *fscroll = (CDKSCROLL *)fselect->scrollField; fselect->highlight = highlight; setCDKScrollHighlight (fscroll, highlight); } chtype getCDKFselectHighlight (CDKFSELECT *fselect) { return fselect->highlight; } /* * This sets the attribute of the directory attribute in the * scrolling list. */ void setCDKFselectDirAttribute (CDKFSELECT *fselect, const char *attribute) { /* Make sure they are not the same. */ if (fselect->dirAttribute != attribute) { freeChar (fselect->dirAttribute); fselect->dirAttribute = copyChar (attribute); setCDKFselectDirContents (fselect); } } char *getCDKFselectDirAttribute (CDKFSELECT *fselect) { return fselect->dirAttribute; } /* * This sets the attribute of the link attribute in the * scrolling list. */ void setCDKFselectLinkAttribute (CDKFSELECT *fselect, const char *attribute) { /* Make sure they are not the same. */ if (fselect->linkAttribute != attribute) { freeChar (fselect->linkAttribute); fselect->linkAttribute = copyChar (attribute); setCDKFselectDirContents (fselect); } } char *getCDKFselectLinkAttribute (CDKFSELECT *fselect) { return fselect->linkAttribute; } /* * This sets the attribute of the link attribute in the * scrolling list. */ void setCDKFselectSocketAttribute (CDKFSELECT *fselect, const char *attribute) { /* Make sure they are not the same. */ if (fselect->sockAttribute != attribute) { freeChar (fselect->sockAttribute); fselect->sockAttribute = copyChar (attribute); setCDKFselectDirContents (fselect); } } char *getCDKFselectSocketAttribute (CDKFSELECT *fselect) { return fselect->sockAttribute; } /* * This sets the attribute of the link attribute in the * scrolling list. */ void setCDKFselectFileAttribute (CDKFSELECT *fselect, const char *attribute) { /* Make sure they are not the same. */ if (fselect->fileAttribute != attribute) { freeChar (fselect->fileAttribute); fselect->fileAttribute = copyChar (attribute); setCDKFselectDirContents (fselect); } } char *getCDKFselectFileAttribute (CDKFSELECT *fselect) { return fselect->fileAttribute; } /* * This sets the box attribute of the widget. */ void setCDKFselectBox (CDKFSELECT *fselect, boolean Box) { ObjOf (fselect)->box = Box; ObjOf (fselect)->borderSize = Box ? 1 : 0; } boolean getCDKFselectBox (CDKFSELECT *fselect) { return ObjOf (fselect)->box; } /* * This sets the contents of the widget */ void setCDKFselectContents (CDKFSELECT *widget, CDK_CSTRING2 list, int listSize) { /* *INDENT-EQLS* */ CDKSCROLL *scrollp = widget->scrollField; CDKENTRY *entry = widget->entryField; if (!createList (widget, list, listSize)) return; /* Set the information in the scrolling list. */ setCDKScroll (scrollp, (CDK_CSTRING2)widget->dirContents, widget->fileCounter, NONUMBERS, scrollp->highlight, ObjOf (scrollp)->box); /* Clean out the entry field. */ setCDKFselectCurrentItem (widget, 0); cleanCDKEntry (entry); /* Redraw the widget. */ eraseCDKFselect (widget); drawCDKFselect (widget, ObjOf (widget)->box); } char **getCDKFselectContents (CDKFSELECT *widget, int *size) { (*size) = widget->fileCounter; return widget->dirContents; } /* * Get/set the current position in the scroll-widget. */ int getCDKFselectCurrentItem (CDKFSELECT *widget) { return getCDKScrollCurrent (widget->scrollField); } void setCDKFselectCurrentItem (CDKFSELECT *widget, int item) { if (widget->fileCounter != 0) { char *data; setCDKScrollCurrent (widget->scrollField, item); data = contentToPath (widget, widget->dirContents[getCDKScrollCurrentItem (widget->scrollField)]); setCDKEntryValue (widget->entryField, data); free (data); } } /* * These functions set the drawing characters of the widget. */ static void _setMyULchar (CDKOBJS *object, chtype character) { CDKFSELECT *fselect = (CDKFSELECT *)object; setCDKEntryULChar (fselect->entryField, character); } static void _setMyURchar (CDKOBJS *object, chtype character) { CDKFSELECT *fselect = (CDKFSELECT *)object; setCDKEntryURChar (fselect->entryField, character); } static void _setMyLLchar (CDKOBJS *object, chtype character) { CDKFSELECT *fselect = (CDKFSELECT *)object; setCDKScrollLLChar (fselect->scrollField, character); } static void _setMyLRchar (CDKOBJS *object, chtype character) { CDKFSELECT *fselect = (CDKFSELECT *)object; setCDKScrollLRChar (fselect->scrollField, character); } static void _setMyVTchar (CDKOBJS *object, chtype character) { CDKFSELECT *fselect = (CDKFSELECT *)object; setCDKEntryVerticalChar (fselect->entryField, character); setCDKScrollVerticalChar (fselect->scrollField, character); } static void _setMyHZchar (CDKOBJS *object, chtype character) { CDKFSELECT *fselect = (CDKFSELECT *)object; setCDKEntryHorizontalChar (fselect->entryField, character); setCDKScrollHorizontalChar (fselect->scrollField, character); } static void _setMyBXattr (CDKOBJS *object, chtype character) { CDKFSELECT *fselect = (CDKFSELECT *)object; setCDKEntryBoxAttribute (fselect->entryField, character); setCDKScrollBoxAttribute (fselect->scrollField, character); } /* * This sets the background attribute of the widget. */ static void _setBKattrFselect (CDKOBJS *object, chtype attrib) { if (object != 0) { CDKFSELECT *widget = (CDKFSELECT *)object; setCDKEntryBackgroundAttrib (widget->entryField, attrib); setCDKScrollBackgroundAttrib (widget->scrollField, attrib); } } /* * This destroys the file selector. */ static void _destroyCDKFselect (CDKOBJS *object) { if (object != 0) { CDKFSELECT *fselect = (CDKFSELECT *)object; cleanCDKObjectBindings (vFSELECT, fselect); /* Free up the character pointers. */ freeChar (fselect->pwd); freeChar (fselect->pathname); freeChar (fselect->dirAttribute); freeChar (fselect->fileAttribute); freeChar (fselect->linkAttribute); freeChar (fselect->sockAttribute); CDKfreeStrings (fselect->dirContents); /* Destroy the other Cdk objects. */ destroyCDKScroll (fselect->scrollField); destroyCDKEntry (fselect->entryField); /* Free up the window pointers. */ deleteCursesWindow (fselect->shadowWin); deleteCursesWindow (fselect->win); /* Clean the key bindings. */ /* Unregister the object. */ unregisterCDKObject (vFSELECT, fselect); } } /* ******************************** * Callback functions. ******************************** */ /* * This is a callback to the scrolling list which displays information * about the current file. (and the whole directory as well) */ static int displayFileInfoCB (EObjectType objectType GCC_UNUSED, void *object, void *clientData, chtype key GCC_UNUSED) { CDKENTRY *entry = (CDKENTRY *)object; CDKFSELECT *fselect = (CDKFSELECT *)clientData; CDKLABEL *infoLabel; struct stat fileStat; #ifdef HAVE_PWD_H struct passwd *pwEnt; struct group *grEnt; #endif char *filename; const char *filetype; char *mesg[10]; char stringMode[15]; int intMode; boolean functionKey; filename = fselect->entryField->info; if (lstat (filename, &fileStat) == 0) { switch (mode2Filetype (fileStat.st_mode)) { case 'l': filetype = "Symbolic Link"; break; case '@': filetype = "Socket"; break; case '-': filetype = "Regular File"; break; case 'd': filetype = "Directory"; break; case 'c': filetype = "Character Device"; break; case 'b': filetype = "Block Device"; break; case '&': filetype = "FIFO Device"; break; default: filetype = "Unknown"; break; } } else { filetype = "Unknown"; } /* Get the user name and group name. */ #ifdef HAVE_PWD_H pwEnt = getpwuid (fileStat.st_uid); grEnt = getgrgid (fileStat.st_gid); #endif /* Convert the mode_t type to both string and int. */ intMode = mode2Char (stringMode, fileStat.st_mode); /* Create the message. */ mesg[0] = format1String ("Directory : %s", fselect->pwd); mesg[1] = format1String ("Filename : %s", filename); #ifdef HAVE_PWD_H mesg[2] = format1StrVal ("Owner : %s (%d)", pwEnt->pw_name, (int)fileStat.st_uid); mesg[3] = format1StrVal ("Group : %s (%d)", grEnt->gr_name, (int)fileStat.st_gid); #else mesg[2] = format1Number ("Owner : (%ld)", (long)fileStat.st_uid); mesg[3] = format1Number ("Group : (%ld)", (long)fileStat.st_gid); #endif mesg[4] = format1StrVal ("Permissions: %s (%o)", stringMode, intMode); mesg[5] = format1Number ("Size : %ld bytes", (long)fileStat.st_size); mesg[6] = format1Date ("Last Access: %s", fileStat.st_atime); mesg[7] = format1Date ("Last Change: %s", fileStat.st_ctime); mesg[8] = format1String ("File Type : %s", filetype); /* Create the pop up label. */ infoLabel = newCDKLabel (entry->obj.screen, CENTER, CENTER, (CDK_CSTRING2)mesg, 9, TRUE, FALSE); drawCDKLabel (infoLabel, TRUE); getchCDKObject (ObjOf (infoLabel), &functionKey); /* Clean up some memory. */ destroyCDKLabel (infoLabel); freeCharList (mesg, 9); /* Redraw the file selector. */ drawCDKFselect (fselect, ObjOf (fselect)->box); return (TRUE); } static char *make_pathname (const char *directory, const char *filename) { size_t need = strlen (filename) + 2; bool root = (strcmp (directory, "/") == 0); char *result; if (!root) need += strlen (directory); if ((result = (char *)malloc (need)) != 0) { if (root) sprintf (result, "/%s", filename); else sprintf (result, "%s/%s", directory, filename); } return result; } /* * Return the plain string that corresponds to an item in dirContents[]. */ static char *contentToPath (CDKFSELECT *fselect, char *content) { chtype *tempChtype; char *tempChar; char *result; int j, j2; tempChtype = char2Chtype (content, &j, &j2); tempChar = chtype2Char (tempChtype); trim1Char (tempChar); /* trim the 'mode' stored on the end */ /* Create the pathname. */ result = make_pathname (fselect->pwd, tempChar); /* Clean up. */ freeChtype (tempChtype); freeChar (tempChar); return result; } /* * This tries to complete the filename. */ static int completeFilenameCB (EObjectType objectType GCC_UNUSED, void *object GCC_UNUSED, void *clientData, chtype key GCC_UNUSED) { /* *INDENT-EQLS* */ CDKFSELECT *fselect = (CDKFSELECT *)clientData; CDKSCROLL *scrollp = fselect->scrollField; CDKENTRY *entry = fselect->entryField; char *filename = copyChar (entry->info); char *mydirname = dirName (filename); char *newFilename = 0; size_t filenameLen = 0; int isDirectory; char **list; /* Make sure the filename is not null/empty. */ if (filename == 0 || (filenameLen = strlen (filename)) == 0) { Beep (); freeChar (filename); freeChar (mydirname); return (TRUE); } /* Try to expand the filename if it starts with a ~ */ if ((newFilename = expandTilde (filename)) != 0) { freeChar (filename); filename = newFilename; setCDKEntryValue (entry, filename); drawCDKEntry (entry, ObjOf (entry)->box); } /* Make sure we can change into the directory. */ isDirectory = chdir (filename); if (chdir (fselect->pwd) != 0) { freeChar (filename); freeChar (mydirname); return FALSE; } setCDKFselect (fselect, isDirectory ? mydirname : filename, fselect->fieldAttribute, fselect->fillerCharacter, fselect->highlight, fselect->dirAttribute, fselect->fileAttribute, fselect->linkAttribute, fselect->sockAttribute, ObjOf (fselect)->box); freeChar (mydirname); /* If we can, change into the directory. */ if (isDirectory) { /* * Set the entry field with the filename so the current * filename selection shows up. */ setCDKEntryValue (entry, filename); drawCDKEntry (entry, ObjOf (entry)->box); } /* Create the file list. */ if ((list = typeMallocN (char *, fselect->fileCounter)) != 0) { int Index, x; for (x = 0; x < fselect->fileCounter; x++) { list[x] = contentToPath (fselect, fselect->dirContents[x]); } /* Look for a unique filename match. */ Index = searchList ((CDK_CSTRING2)list, fselect->fileCounter, filename); /* If the index is less than zero, return we didn't find a match. */ if (Index < 0) { Beep (); } else { /* Move to the current item in the scrolling list. */ int difference = Index - scrollp->currentItem; int absoluteDifference = abs (difference); if (difference < 0) { for (x = 0; x < absoluteDifference; x++) { injectMyScroller (fselect, KEY_UP); } } else if (difference > 0) { for (x = 0; x < absoluteDifference; x++) { injectMyScroller (fselect, KEY_DOWN); } } drawMyScroller (fselect); /* Ok, we found a match, is the next item similar? */ if (Index + 1 < fselect->fileCounter && 0 != list[Index + 1] && 0 == strncmp (list[Index + 1], filename, filenameLen)) { int currentIndex = Index; int baseChars = (int)filenameLen; int matches = 0; /* Determine the number of files which match. */ while (currentIndex < fselect->fileCounter) { if (list[currentIndex] != 0) { if (strncmp (list[currentIndex], filename, filenameLen) == 0) { matches++; } } currentIndex++; } /* Start looking for the common base characters. */ for (;;) { int secondaryMatches = 0; for (x = Index; x < Index + matches; x++) { if (list[Index][baseChars] == list[x][baseChars]) { secondaryMatches++; } } if (secondaryMatches != matches) { Beep (); break; } /* Inject the character into the entry field. */ (void)injectCDKEntry (fselect->entryField, (chtype)list[Index][baseChars]); baseChars++; } } else { /* Set the entry field with the found item. */ setCDKEntryValue (entry, list[Index]); drawCDKEntry (entry, ObjOf (entry)->box); } } freeCharList (list, (unsigned)fselect->fileCounter); free (list); } freeChar (filename); return (TRUE); } /* * This allows the user to delete a file. */ void deleteFileCB (EObjectType objectType GCC_UNUSED, void *object, void *clientData) { CDKSCROLL *fscroll = (CDKSCROLL *)object; CDKFSELECT *fselect = (CDKFSELECT *)clientData; const char *buttons[] = { "No", "Yes" }; CDKDIALOG *question; char *mesg[10], *filename; /* Get the filename which is to be deleted. */ filename = chtype2Char (fscroll->item[fscroll->currentItem]); trim1Char (filename); /* Create the dialog message. */ mesg[0] = copyChar ("Are you sure you want to delete the file:"); mesg[1] = format1String ("\"%s\"?", filename); /* Create the dialog box. */ question = newCDKDialog (ScreenOf (fselect), CENTER, CENTER, (CDK_CSTRING2)mesg, 2, (CDK_CSTRING2)buttons, 2, A_REVERSE, TRUE, TRUE, FALSE); freeCharList (mesg, 2); /* If the said yes then try to nuke it. */ if (activateCDKDialog (question, 0) == 1) { /* If we were successful, reload the scrolling list. */ if (unlink (filename) == 0) { /* Set the file selector information. */ setCDKFselect (fselect, fselect->pwd, fselect->fieldAttribute, fselect->fillerCharacter, fselect->highlight, fselect->dirAttribute, fselect->fileAttribute, fselect->linkAttribute, fselect->sockAttribute, ObjOf (fselect)->box); } else { /* Pop up a message. */ mesg[0] = copyChar (errorMessage ("Cannot delete file: %s")); mesg[1] = copyChar (" "); mesg[2] = copyChar ("Press any key to continue."); popupLabel (ScreenOf (fselect), (CDK_CSTRING2)mesg, 3); freeCharList (mesg, 3); } } /* Clean up. */ destroyCDKDialog (question); /* Redraw the file selector. */ drawCDKFselect (fselect, ObjOf (fselect)->box); } /* * This function sets the pre-process function. */ void setCDKFselectPreProcess (CDKFSELECT *fselect, PROCESSFN callback, void *data) { setCDKEntryPreProcess (fselect->entryField, callback, data); setCDKScrollPreProcess (fselect->scrollField, callback, data); } /* * This function sets the post-process function. */ void setCDKFselectPostProcess (CDKFSELECT *fselect, PROCESSFN callback, void *data) { setCDKEntryPostProcess (fselect->entryField, callback, data); setCDKScrollPostProcess (fselect->scrollField, callback, data); } /* * Start of callback functions. */ static int fselectAdjustScrollCB (EObjectType objectType GCC_UNUSED, void *object GCC_UNUSED, void *clientData, chtype key) { /* *INDENT-EQLS* */ CDKFSELECT *fselect = (CDKFSELECT *)clientData; CDKSCROLL *scrollp = (CDKSCROLL *)fselect->scrollField; CDKENTRY *entry = (CDKENTRY *)fselect->entryField; if (scrollp->listSize > 0) { char *current; char *temp; /* Move the scrolling list. */ injectMyScroller (fselect, key); /* Get the currently highlighted filename. */ current = chtype2Char (scrollp->item[scrollp->currentItem]); trim1Char (current); temp = make_pathname (fselect->pwd, current); /* Set the value in the entry field. */ setCDKEntryValue (entry, temp); drawCDKEntry (entry, ObjOf (entry)->box); freeChar (current); freeChar (temp); return (TRUE); } Beep (); return (FALSE); } /* * trim the 'mode' from a copy of a dirContents[] entry. */ static char *trim1Char (char *source) { size_t len; if ((len = strlen (source)) != 0) source[--len] = '\0'; return source; } static char *format1Date (const char *format, time_t value) { char *result; char *temp = ctime (&value); if ((result = (char *)malloc (strlen (format) + strlen (temp) + 1)) != 0) { sprintf (result, format, trim1Char (temp)); } return result; } static char *format1Number (const char *format, long value) { char *result; if ((result = (char *)malloc (strlen (format) + 20)) != 0) sprintf (result, format, value); return result; } static char *format3String (const char *format, const char *s1, const char *s2, const char *s3) { char *result; if ((result = (char *)malloc (strlen (format) + strlen (s1) + strlen (s2) + strlen (s3))) != 0) sprintf (result, format, s1, s2, s3); return result; } static char *format1String (const char *format, const char *string) { char *result; if ((result = (char *)malloc (strlen (format) + strlen (string))) != 0) sprintf (result, format, string); return result; } static char *format1StrVal (const char *format, const char *string, int value) { char *result; if ((result = (char *)malloc (strlen (format) + strlen (string) + 20)) != 0) sprintf (result, format, string, value); return result; } static char *errorMessage (const char *format) { char *message; #ifdef HAVE_STRERROR message = strerror (errno); #else message = "Unknown reason."; #endif return format1String (format, message); } /* * This takes a ~ type account name and returns the full pathname. */ static char *expandTilde (const char *filename) { char *result = 0; char *account; char *pathname; int len; /* Make sure the filename is not null/empty, and begins with a tilde */ if ((filename != 0) && (len = (int)strlen (filename)) != 0 && filename[0] == '~' && (account = copyChar (filename)) != 0 && (pathname = copyChar (filename)) != 0) { bool slash = FALSE; const char *home; int x; int len_a = 0; int len_p = 0; struct passwd *accountInfo; /* Find the account name in the filename. */ for (x = 1; x < len; x++) { if (filename[x] == '/' && !slash) { slash = TRUE; } else if (slash) { pathname[len_p++] = filename[x]; } else { account[len_a++] = filename[x]; } } account[len_a] = '\0'; pathname[len_p] = '\0'; home = 0; #ifdef HAVE_PWD_H if (strlen (account) != 0 && (accountInfo = getpwnam (account)) != 0) { home = accountInfo->pw_dir; } #endif if (home == 0 || *home == '\0') home = getenv ("HOME"); if (home == 0 || *home == '\0') home = "/"; /* * Construct the full pathname. We do this because someone * may have a pathname at the end of the account name * and we want to keep it. */ result = make_pathname (home, pathname); freeChar (account); freeChar (pathname); } return result; } /* * Store the name of the current working directory. */ static void setPWD (CDKFSELECT *fselect) { char buffer[512]; freeChar (fselect->pwd); if (getcwd (buffer, sizeof (buffer)) == 0) strcpy (buffer, "."); fselect->pwd = copyChar (buffer); } static void destroyInfo (CDKFSELECT *widget) { CDKfreeStrings (widget->dirContents); widget->dirContents = 0; widget->fileCounter = 0; } static int createList (CDKFSELECT *widget, CDK_CSTRING2 list, int listSize) { int status = 0; if (listSize >= 0) { char **newlist = typeCallocN (char *, listSize + 1); if (newlist != 0) { int x; /* Copy in the new information. */ status = 1; for (x = 0; x < listSize; x++) { if ((newlist[x] = copyChar (list[x])) == 0) { status = 0; break; } } if (status) { destroyInfo (widget); widget->fileCounter = listSize; widget->dirContents = newlist; } else { CDKfreeStrings (newlist); } } } else { destroyInfo (widget); status = TRUE; } return status; } static void _focusCDKFselect (CDKOBJS *object) { CDKFSELECT *widget = (CDKFSELECT *)object; FocusObj (ObjOf (widget->entryField)); } static void _unfocusCDKFselect (CDKOBJS *object) { CDKFSELECT *widget = (CDKFSELECT *)object; UnfocusObj (ObjOf (widget->entryField)); } dummyRefreshData (Fselect) dummySaveData (Fselect)