/* $XConsortium: commands.c,v 1.33 91/10/21 14:32:18 eswu Exp $ */ /* * COPYRIGHT 1987 * DIGITAL EQUIPMENT CORPORATION * MAYNARD, MASSACHUSETTS * ALL RIGHTS RESERVED. * * THE INFORMATION IN THIS SOFTWARE IS SUBJECT TO CHANGE WITHOUT NOTICE AND * SHOULD NOT BE CONSTRUED AS A COMMITMENT BY DIGITAL EQUIPMENT CORPORATION. * DIGITAL MAKES NO REPRESENTATIONS ABOUT THE SUITABILITY OF THIS SOFTWARE FOR * ANY PURPOSE. IT IS SUPPLIED "AS IS" WITHOUT EXPRESS OR IMPLIED WARRANTY. * * IF THE SOFTWARE IS MODIFIED IN A MANNER CREATING DERIVATIVE COPYRIGHT RIGHTS, * APPROPRIATE LEGENDS MAY BE PLACED ON THE DERIVATIVE WORK IN ADDITION TO THAT * SET FORTH ABOVE. * * * 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 Digital Equipment Corporation not be * used in advertising or publicity pertaining to distribution of the software * without specific, written prior permission. */ /* $XFree86: xc/programs/xedit/commands.c,v 1.29tsi Exp $ */ #include #include #include "xedit.h" #include #include #include #include #include #include #include #include #include #include /* Turn a NULL pointer string into an empty string */ #define NULLSTR(x) (((x)!=NULL)?(x):("")) #define Error(x) { printf x ; exit(EXIT_FAILURE); } #define Assertion(expr, msg) { if (!(expr)) { Error msg } } #define Log(x) { if (True) printf x; } void ResetSourceChanged(xedit_flist_item*); static void ResetDC(Widget, XtPointer, XtPointer); static void AddDoubleClickCallback(Widget, Bool); static Bool ReallyDoLoad(char*, char*); static char *makeBackupName(String, String, unsigned); /* * External */ extern void _XawTextShowPosition(TextWidget); extern Widget scratch, texts[3], labels[3]; #define DC_UNSAVED (1 << 0) #define DC_LOADED (1 << 1) #define DC_CLOBBER (1 << 2) #define DC_KILL (1 << 3) #define DC_SAVE (1 << 4) #define DC_NEWER (1 << 5) static int dc_state; static void AddDoubleClickCallback(Widget w, Bool state) { if (state) XtAddCallback(w, XtNcallback, ResetDC, NULL); else XtRemoveCallback(w, XtNcallback, ResetDC, NULL); } /* Function Name: ResetDC * Description: Resets the double click flag. * Arguments: w - the text widget. * junk, garbage - *** NOT USED *** * Returns: none. */ /* ARGSUSED */ static void ResetDC(Widget w, XtPointer junk, XtPointer garbage) { AddDoubleClickCallback(w, FALSE); } /*ARGSUSED*/ void QuitAction(Widget w, XEvent *event, String *params, Cardinal *num_params) { DoQuit(w, NULL, NULL); } /*ARGSUSED*/ void DoQuit(Widget w, XtPointer client_data, XtPointer call_data) { unsigned i; Bool source_changed = False; if (!(dc_state & DC_UNSAVED)) { for (i = 0; i < flist.num_itens; i++) if (flist.itens[i]->flags & CHANGED_BIT) { source_changed = True; break; } } if (!source_changed) { #ifndef __UNIXOS2__ XeditLispCleanUp(); #endif exit(0); } XeditPrintf("Unsaved changes. Save them, or Quit again.\n"); Feep(); dc_state |= DC_UNSAVED; AddDoubleClickCallback(XawTextGetSource(textwindow), True); } static char * makeBackupName(String buf, String filename, unsigned len) { if (app_resources.backupNamePrefix && strlen(app_resources.backupNamePrefix)) { if (strchr(app_resources.backupNamePrefix, '/')) XmuSnprintf(buf, len, "%s%s%s", app_resources.backupNamePrefix, filename, app_resources.backupNameSuffix); else { char fname[BUFSIZ]; char *name, ch; strncpy(fname, filename, sizeof(fname) - 1); fname[sizeof(fname) - 1] = '\0'; if ((name = strrchr(fname, '/')) != NULL) ++name; else name = filename; ch = *name; *name = '\0'; ++name; XmuSnprintf(buf, len, "%s%s%c%s%s", fname, app_resources.backupNamePrefix, ch, name, app_resources.backupNameSuffix); } } else XmuSnprintf(buf, len, "%s%s", filename, app_resources.backupNameSuffix); return (strcmp(filename, buf) ? buf : NULL); } /*ARGSUSED*/ void SaveFile(Widget w, XEvent *event, String *params, Cardinal *num_params) { if (line_edit) { /* Don't try to save buffer with regex string. * Call CancelFindFile() to leave line_edit mode. */ XeditPrintf("Save: Leaving line edit mode -- nothing saved.\n"); CancelFindFile(w, event, params, num_params); Feep(); } else DoSave(w, NULL, NULL); } /*ARGSUSED*/ void DoSave(Widget w, XtPointer client_data, XtPointer call_data) { String name = GetString(filenamewindow); String filename = ResolveName(name); FileAccess file_access; xedit_flist_item *item; Boolean exists; Widget source = XawTextGetSource(textwindow); char buffer[BUFSIZ]; struct stat st; static const char *nothing_saved = " -- nothing saved.\n"; if (!filename) { XmuSnprintf(buffer, sizeof(buffer), "%s%s", "Save: Can't resolve pathname", nothing_saved); goto error; } else if (*name == '\0') { XmuSnprintf(buffer, sizeof(buffer), "%s%s", "Save: No filename specified", nothing_saved); goto error; } item = FindTextSource(NULL, filename); if (item != NULL && item->source != source) { if (!(dc_state & DC_LOADED)) { XmuSnprintf(buffer, sizeof(buffer), "%s%s%s%s", "Save: file ", name, " is already loaded, " "Save again to unload it", nothing_saved); Feep(); dc_state |= DC_LOADED; AddDoubleClickCallback(XawTextGetSource(textwindow), True); goto error; } else { KillTextSource(item); item = FindTextSource(source = XawTextGetSource(textwindow), NULL); dc_state &= ~DC_LOADED; } } else if (item && !(item->flags & CHANGED_BIT)) { if (!(dc_state & DC_SAVE)) { XmuSnprintf(buffer, sizeof(buffer), "%s%s", "Save: No changes need to be saved, " "save again to override", nothing_saved); Feep(); dc_state |= DC_SAVE; AddDoubleClickCallback(XawTextGetSource(textwindow), True); goto error; } else dc_state &= ~DC_SAVE; } file_access = CheckFilePermissions(filename, &exists); if (exists) { if (stat(filename, &st) != 0) { XmuSnprintf(buffer, sizeof(buffer), "%s%s%s", "Save: cannot stat ", name, nothing_saved); goto error; } else if (!S_ISREG(st.st_mode)) { XmuSnprintf(buffer, sizeof(buffer), "%s%s%s%s", "Save: file ", name, "is not a regular file", nothing_saved); goto error; } } if (!item || strcmp(item->filename, filename)) { if (file_access == WRITE_OK && exists) { if (!(dc_state & DC_CLOBBER)) { XmuSnprintf(buffer, sizeof(buffer), "%s%s%s%s", "Save: file ", name, " already exists, " "save again to override", nothing_saved); Feep(); dc_state |= DC_CLOBBER; AddDoubleClickCallback(XawTextGetSource(textwindow), True); goto error; } else dc_state &= ~DC_CLOBBER; } if (!item) item = FindTextSource(source, NULL); } if (item && item->mtime && exists && item->mtime < st.st_mtime) { if (!(dc_state & DC_NEWER)) { XmuSnprintf(buffer, sizeof(buffer), "%s%s", "Save: Newer file exists, " "save again to override", nothing_saved); Feep(); dc_state |= DC_NEWER; AddDoubleClickCallback(XawTextGetSource(textwindow), True); goto error; } else dc_state &= DC_NEWER; } if (app_resources.enableBackups && exists) { char backup_file[BUFSIZ]; if (makeBackupName(backup_file, filename, sizeof(backup_file)) == NULL || rename(filename, backup_file) != 0) { XeditPrintf("Error backing up file: %s\n", filename); } } switch (file_access = MaybeCreateFile(filename)) { case NO_READ: case READ_OK: XeditPrintf("File %s could not be opened for writing.\n", name); Feep(); break; case WRITE_OK: if (XawAsciiSaveAsFile(source, filename)) { int i; Arg args[1]; XmuSnprintf(buffer, sizeof(buffer), "%s Read - Write", name); XtSetArg(args[0], XtNlabel, buffer); for (i = 0; i < 3; i++) if (XawTextGetSource(texts[i]) == source) XtSetValues(labels[i], args, 1); XeditPrintf("Saved file: %s\n", name); if (item && item->source != scratch) { XtSetArg(args[0], XtNlabel, filename); XtSetValues(item->sme, args, 1); XtSetArg(args[0], XtNeditType, XawtextEdit); XtSetValues(item->source, args, 1); XtFree(item->name); XtFree(item->filename); item->name = XtNewString(name); item->filename = XtNewString(filename); item->flags = EXISTS_BIT; } else { item = flist.itens[0]; XtRemoveCallback(scratch, XtNcallback, SourceChanged, (XtPointer)item); item->source = scratch = XtVaCreateWidget("textSource", international ? multiSrcObjectClass : asciiSrcObjectClass, topwindow, XtNtype, XawAsciiFile, XtNeditType, XawtextEdit, NULL, NULL); ResetSourceChanged(item); XtAddCallback(scratch, XtNcallback, SourceChanged, (XtPointer)item); item = AddTextSource(source, name, filename, EXISTS_BIT, file_access); XtAddCallback(item->source, XtNcallback, SourceChanged, (XtPointer)item); } /* Keep file protection mode */ if (item->mode) chmod(filename, item->mode); /* Remember time of last modification */ if (stat(filename, &st) == 0) item->mtime = st.st_mtime; item->flags |= EXISTS_BIT; ResetSourceChanged(item); } else { XeditPrintf("Error saving file: %s\n", name); Feep(); } break; default: Feep(); break; } return; error: XeditPrintf("%s", buffer); Feep(); } /*ARGSUSED*/ void DoLoad(Widget w, XtPointer client_data, XtPointer call_data) { if (ReallyDoLoad(GetString(filenamewindow), ResolveName(NULL))) { SwitchDirWindow(False); XtSetKeyboardFocus(topwindow, textwindow); } } Bool LoadFileInTextwindow(char *name, char *resolved_name) { return (ReallyDoLoad(name, resolved_name)); } static Bool ReallyDoLoad(char *name, char *filename) { Arg args[5]; Cardinal num_args = 0; xedit_flist_item *item; Widget source = XawTextGetSource(textwindow); if (!filename) { XeditPrintf("Load: Can't resolve pathname.\n"); Feep(); return (False); } else if (*name == '\0') { XeditPrintf("Load: No file specified.\n"); Feep(); } if ((item = FindTextSource(NULL, filename)) != NULL) { SwitchTextSource(item); return (True); } else { struct stat st; if (stat(filename, &st) == 0 && !S_ISREG(st.st_mode)) { if (S_ISDIR(st.st_mode)) { char path[BUFSIZ + 1]; strncpy(path, filename, sizeof(path) - 2); path[sizeof(path) - 2] = '\0'; if (*path) { if (path[strlen(path) - 1] != '/') strcat(path, "/"); } else strcpy(path, "./"); XtSetArg(args[0], XtNlabel, ""); XtSetValues(dirlabel, args, 1); SwitchDirWindow(True); DirWindowCB(dirwindow, path, NULL); return (False); } } } { Boolean exists; int flags; FileAccess file_access; switch( file_access = CheckFilePermissions(filename, &exists) ) { case NO_READ: if (exists) XeditPrintf("File %s, %s", name, "exists, and could not be opened for reading.\n"); else XeditPrintf("File %s %s %s", name, "does not exist, and", "the directory could not be opened for writing.\n"); Feep(); return (False); case READ_OK: XtSetArg(args[num_args], XtNeditType, XawtextRead); num_args++; XeditPrintf("File %s opened READ ONLY.\n", name); break; case WRITE_OK: XtSetArg(args[num_args], XtNeditType, XawtextEdit); num_args++; XeditPrintf("File %s opened read - write.\n", name); break; default: Feep(); return (False); } if (exists) { flags = EXISTS_BIT; XtSetArg(args[num_args], XtNstring, filename); num_args++; } else { flags = 0; XtSetArg(args[num_args], XtNstring, NULL); num_args++; } source = XtVaCreateWidget("textSource", international ? multiSrcObjectClass : asciiSrcObjectClass, topwindow, XtNtype, XawAsciiFile, XtNeditType, XawtextEdit, NULL, NULL); XtSetValues(source, args, num_args); item = AddTextSource(source, name, filename, flags, file_access); XtAddCallback(item->source, XtNcallback, SourceChanged, (XtPointer)item); if (exists && file_access == WRITE_OK) { struct stat st; if (stat(item->filename, &st) == 0) { item->mode = st.st_mode & (S_IRWXU | S_IRWXG | S_IRWXO); item->mtime = st.st_mtime; } } SwitchTextSource(item); ResetSourceChanged(item); } return (True); } /* Function Name: SourceChanged * Description: A callback routine called when the source has changed. * Arguments: w - the text source that has changed. * client_data - xedit_flist_item associated with text buffer. * call_data - NULL is unchanged * Returns: none. */ /*ARGSUSED*/ void SourceChanged(Widget w, XtPointer client_data, XtPointer call_data) { xedit_flist_item *item = (xedit_flist_item*)client_data; Bool changed = (Bool)(long)call_data; if (changed) { if (item->flags & CHANGED_BIT) return; item->flags |= CHANGED_BIT; } else { if (item->flags & CHANGED_BIT) ResetSourceChanged(item); return; } if (flist.pixmap) { Arg args[1]; Cardinal num_args; int i; num_args = 0; XtSetArg(args[num_args], XtNleftBitmap, flist.pixmap); ++num_args; XtSetValues(item->sme, args, num_args); for (i = 0; i < 3; i++) if (XawTextGetSource(texts[i]) == item->source) XtSetValues(labels[i], args, num_args); } } /* Function Name: ResetSourceChanged. * Description: Sets the source changed to FALSE, and * registers a callback to set it to TRUE when * the source has changed. * Arguments: item - item with widget to register the callback on. * Returns: none. */ void ResetSourceChanged(xedit_flist_item *item) { Arg args[1]; Cardinal num_args; int i; num_args = 0; XtSetArg(args[num_args], XtNleftBitmap, None); ++num_args; XtSetValues(item->sme, args, num_args); dc_state = 0; for (i = 0; i < 3; i++) { if (XawTextGetSource(texts[i]) == item->source) XtSetValues(labels[i], args, num_args); AddDoubleClickCallback(XawTextGetSource(texts[i]), False); } num_args = 0; XtSetArg(args[num_args], XtNsourceChanged, False); ++num_args; XtSetValues(item->source, args, num_args); item->flags &= ~CHANGED_BIT; } /*ARGSUSED*/ void KillFile(Widget w, XEvent *event, String *params, Cardinal *num_params) { xedit_flist_item *item = FindTextSource(XawTextGetSource(textwindow), NULL); if (item->source == scratch) { Feep(); return; } if (item->flags & CHANGED_BIT) { if (!(dc_state & DC_KILL)) { XeditPrintf("Kill: Unsaved changes. Kill again to override.\n"); Feep(); dc_state |= DC_KILL; AddDoubleClickCallback(XawTextGetSource(textwindow), True); return; } dc_state &= ~DC_KILL; } KillTextSource(item); } /*ARGSUSED*/ void FindFile(Widget w, XEvent *event, String *params, Cardinal *num_params) { char *string; char *slash; XawTextBlock block; XawTextPosition end = XawTextSourceScan(XawTextGetSource(filenamewindow), 0, XawstAll, XawsdRight, 1, True); slash = NULL; if (!line_edit) { string = GetString(filenamewindow); if (string) slash = strrchr(string, '/'); } else { string = ""; line_edit = False; } block.firstPos = 0; block.format = FMT8BIT; block.ptr = string; block.length = slash ? slash - string + 1 : 0; if (block.length != end) XawTextReplace(filenamewindow, 0, end, &block); XawTextSetInsertionPoint(filenamewindow, end); XtSetKeyboardFocus(topwindow, filenamewindow); } /*ARGSUSED*/ void LoadFile(Widget w, XEvent *event, String *params, Cardinal *num_params) { if (line_edit) LineEdit(textwindow); else if (ReallyDoLoad(GetString(filenamewindow), ResolveName(NULL))) { SwitchDirWindow(False); XtSetKeyboardFocus(topwindow, textwindow); } } /*ARGSUSED*/ void CancelFindFile(Widget w, XEvent *event, String *params, Cardinal *num_params) { Arg args[1]; xedit_flist_item *item; XtSetKeyboardFocus(topwindow, textwindow); item = FindTextSource(XawTextGetSource(textwindow), NULL); if (item->source != scratch) XtSetArg(args[0], XtNstring, item->name); else XtSetArg(args[0], XtNstring, NULL); XtSetValues(filenamewindow, args, 1); /* XXX This probably should be done by the TextWidget, i.e. notice * if the cursor became inivisible due to an horizontal scroll */ _XawTextShowPosition((TextWidget)filenamewindow); if (XtIsManaged(XtParent(dirwindow))) SwitchDirWindow(False); line_edit = False; } static int compar(_Xconst void *a, _Xconst void *b) { return (strcmp(*(char **)a, *(char **)b)); } /*ARGSUSED*/ void FileCompletion(Widget w, XEvent *event, String *params, Cardinal *num_params) { XawTextBlock block; String text; int length; char **matches, *save, *dir_name, *file_name, match[257]; unsigned n_matches, len, mlen, buflen; DIR *dir; Bool changed, slash = False, has_dot = False; #define SM_NEVER 0 #define SM_HINT 1 #define SM_ALWAYS 2 int show_matches; text = GetString(filenamewindow); if (!text) { Feep(); return; } else if (line_edit) { Feep(); line_edit = 0; } { XawTextPosition pos = XawTextGetInsertionPoint(w); char *cslash = strchr(&text[pos], '/'), *cdot = strchr(&text[pos], '.'); if (cslash != NULL || cdot != NULL) { if (cslash != NULL && (cdot == NULL || cdot > cslash)) { length = cslash - text; slash = True; } else { length = cdot - text; has_dot = True; } } else length = strlen(text); } if (*num_params == 1 && length == strlen(text)) { switch (params[0][0]) { case 'n': /* Never */ case 'N': show_matches = SM_NEVER; break; case 'h': /* Hint */ case 'H': show_matches = SM_HINT; break; case 'a': /* Always */ case 'A': show_matches = SM_ALWAYS; break; default: show_matches = SM_NEVER; XtAppWarning(XtWidgetToApplicationContext(w), "Bad argument to file-completion, " "must be Never, Hint or Always"); break; } } else show_matches = SM_NEVER; matches = NULL; n_matches = buflen = 0; save = XtMalloc(length + 1); memmove(save, text, length); save[length] = '\0'; if (save[0] == '~' && save[1]) { char *slash2 = strchr(save, '/'); if (slash2) { struct passwd *pw; char home[BUFSIZ]; char *name; int slen = strlen(save), diff = slash2 - save; *slash2 = '\0'; name = save + 1; if (strlen(name) != 0) pw = getpwnam(name); else pw = getpwuid(getuid()); if (pw) { char fname[BUFSIZ]; int hlen; strncpy(home, pw->pw_dir, sizeof(home) - 1); home[sizeof(home) - 1] = '\0'; hlen = strlen(home); strncpy(fname, slash2 + 1, sizeof(fname) - 1); fname[sizeof(fname) - 1] = '\0'; save = XtRealloc(save, slen - diff + hlen + 2); (void)memmove(save, home, hlen); save[hlen] = '/'; strcpy(&save[hlen + 1], fname); /* expand directory */ block.length = strlen(save); block.ptr = save; block.firstPos = 0; block.format = FMT8BIT; XawTextReplace(filenamewindow, 0, length, &block); XawTextSetInsertionPoint(filenamewindow, length = block.length); } else *slash2 = '/'; } } if ((file_name = strrchr(save, '/')) != NULL) { *file_name = '\0'; ++file_name; dir_name = save; if (!file_name[0]) slash = True; if (!dir_name[0]) dir_name = "/"; } else { dir_name = "."; file_name = save; } len = strlen(file_name); if ((dir = opendir(dir_name)) != NULL) { char path[BUFSIZ], *pptr; struct dirent *ent; int isdir = 0, first = 1, bytes; XmuSnprintf(path, sizeof(path), "%s/", dir_name); pptr = path + strlen(path); bytes = sizeof(path) - (pptr - path) - 1; mlen = 0; match[0] = '\0'; while ((ent = readdir(dir)) != NULL) { unsigned d_namlen = strlen(ent->d_name); if (strcmp(ent->d_name, ".") == 0 || strcmp(ent->d_name, "..") == 0) continue; if (d_namlen >= len && strncmp(ent->d_name, file_name, len) == 0) { char *tmp = &(ent->d_name[len]), *mat = match; struct stat st; Bool is_dir = FALSE; strncpy(pptr, ent->d_name, bytes); pptr[bytes] = '\0'; if (stat(path, &st) != 0) /* Should check errno, may be a broken symbolic link * a directory with r-- permission, etc */ continue; else if (first || show_matches != SM_NEVER) { is_dir = S_ISDIR(st.st_mode); } if (first) { strncpy(match, tmp, sizeof(match) - 1); match[sizeof(match) - 2] = '\0'; mlen = strlen(match); first = 0; isdir = is_dir; } else { while (*tmp && *mat && *tmp++ == *mat) ++mat; if (mlen > mat - match) { mlen = mat - match; match[mlen] = '\0'; } } if (show_matches != SM_NEVER) { matches = (char **)XtRealloc((char*)matches, sizeof(char**) * (n_matches + 1)); buflen += d_namlen + 1; if (is_dir) { matches[n_matches] = XtMalloc(d_namlen + 2); strcpy(matches[n_matches], ent->d_name); strcat(matches[n_matches], "/"); ++buflen; } else matches[n_matches] = XtNewString(ent->d_name); } else if (mlen == 0 && n_matches >= 1) { ++n_matches; break; } ++n_matches; } } closedir(dir); changed = mlen != 0; if (first || n_matches) { Bool free_matches = True, add_slash = n_matches == 1 && isdir && !slash; if (mlen && has_dot && match[mlen - 1] == '.') --mlen; if (mlen || add_slash) { XawTextPosition pos; block.firstPos = 0; block.format = FMT8BIT; if (mlen) { pos = length; block.length = mlen; block.ptr = match; XawTextReplace(filenamewindow, pos, pos, &block); XawTextSetInsertionPoint(filenamewindow, pos + block.length); } if (add_slash) { XawTextPosition actual = XawTextGetInsertionPoint(w); pos = XawTextSourceScan(XawTextGetSource(w), 0, XawstAll, XawsdRight, 1, True); block.length = 1; block.ptr = "/"; XawTextReplace(filenamewindow, pos, pos, &block); if (actual == pos) XawTextSetInsertionPoint(filenamewindow, pos + 1); } } else if (n_matches != 1 || isdir) { if (show_matches == SM_NEVER) Feep(); } if (show_matches != SM_NEVER) { if (show_matches == SM_ALWAYS || (!changed && n_matches != 1)) { char **list = NULL, *label; int n_list; Arg args[2]; XtSetArg(args[0], XtNlist, &list); XtSetArg(args[1], XtNnumberStrings, &n_list); XtGetValues(dirwindow, args, 2); matches = (char **)XtRealloc((char*)matches, sizeof(char**) * (n_matches + 2)); matches[n_matches++] = XtNewString("./"); matches[n_matches++] = XtNewString("../"); qsort(matches, n_matches, sizeof(char*), compar); XtSetArg(args[0], XtNlist, matches); XtSetArg(args[1], XtNnumberStrings, n_matches); XtSetValues(dirwindow, args, 2); if (n_list > 0 && (n_list != 1 || list[0] != XtName(dirwindow))) { while (--n_list > -1) XtFree(list[n_list]); XtFree((char*)list); } label = ResolveName(dir_name); XtSetArg(args[0], XtNlabel, label); XtSetValues(dirlabel, args, 1); SwitchDirWindow(True); free_matches = False; } } if (free_matches && matches) { while (--n_matches > -1) XtFree(matches[n_matches]); XtFree((char*)matches); } } else Feep(); } else Feep(); XtFree(save); } /*ARGSUSED*/ void DirWindowCB(Widget w, XtPointer user_data, XtPointer call_data) { XawListReturnStruct *file_info = (XawListReturnStruct *)call_data; char *dir_name, *string, path[BUFSIZ]; Arg args[2]; if (file_info == NULL) string = (char *)user_data; else string = file_info->string; XtSetArg(args[0], XtNlabel, &dir_name); XtGetValues(dirlabel, args, 1); if (*dir_name == '\0') { strncpy(path, string, sizeof(path) - 1); path[sizeof(path) - 1] = '\0'; } else if (strcmp(dir_name, "/") == 0) XmuSnprintf(path, sizeof(path), "/%s", string); else XmuSnprintf(path, sizeof(path), "%s/%s", dir_name, string); if (*string && string[strlen(string) - 1] == '/') { DIR *dir; if ((dir = opendir(path)) != NULL) { struct dirent *ent; struct stat st; unsigned d_namlen; Bool isdir; char **entries = NULL, **list = NULL; int n_entries = 0, n_list = 0; char *label, *pptr = path + strlen(path); int bytes = sizeof(path) - (pptr - path) - 1; while ((ent = readdir(dir)) != NULL) { d_namlen = strlen(ent->d_name); strncpy(pptr, ent->d_name, bytes); pptr[bytes] = '\0'; if (stat(path, &st) != 0) /* Should check errno, may be a broken symbolic link * a directory with r-- permission, etc */ continue; else isdir = S_ISDIR(st.st_mode); entries = (char **)XtRealloc((char*)entries, sizeof(char*) * (n_entries + 1)); if (isdir) { entries[n_entries] = XtMalloc(d_namlen + 2); strcpy(entries[n_entries], ent->d_name); strcat(entries[n_entries], "/"); } else entries[n_entries] = XtNewString(ent->d_name); ++n_entries; } closedir(dir); XtSetArg(args[0], XtNlist, &list); XtSetArg(args[1], XtNnumberStrings, &n_list); XtGetValues(dirwindow, args, 2); if (n_entries == 0) { entries = (char**)XtMalloc(sizeof(char*) * 2); /* Directory has read but not execute permission? */ entries[n_entries++] = XtNewString("./"); entries[n_entries++] = XtNewString("../"); } qsort(entries, n_entries, sizeof(char*), compar); XtSetArg(args[0], XtNlist, entries); XtSetArg(args[1], XtNnumberStrings, n_entries); XtSetValues(dirwindow, args, 2); if (n_list > 0 && (n_list != 1 || list[0] != XtName(dirwindow))) { while (--n_list > -1) XtFree(list[n_list]); XtFree((char*)list); } *pptr = '\0'; if ((label = ResolveName(path)) == NULL) { Feep(); label = path; } XtSetArg(args[0], XtNlabel, label); XtSetValues(dirlabel, args, 1); strncpy(path, label, sizeof(path) - 2); if (*path && path[strlen(path) - 1] != '/') strcat(path, "/"); XtSetArg(args[0], XtNstring, path); XtSetValues(filenamewindow, args, 1); XtSetKeyboardFocus(topwindow, filenamewindow); XawTextSetInsertionPoint(filenamewindow, strlen(path)); } else Feep(); } else { (void)ReallyDoLoad(path, ResolveName(path)); SwitchDirWindow(False); XtSetKeyboardFocus(topwindow, textwindow); } }