/***************************************************************************/ /***************************************************************************/ /* */ /* (c) 1995-1999. The Regents of the University of California. All */ /* rights reserved. */ /* */ /* This work was produced at the University of California, Lawrence */ /* Livermore National Laboratory (UC LLNL) under contract no. */ /* W-7405-ENG-48 (Contract 48) between the U.S. Department of Energy */ /* (DOE) and The Regents of the University of California (University) */ /* for the operation of UC LLNL. Copyright is reserved to the */ /* University for purposes of controlled dissemination, */ /* commercialization through formal licensing, or other disposition */ /* under terms of Contract 48; DOE policies, regulations and orders; */ /* and U.S. statutes. The rights of the Federal Government are */ /* reserved under Contract 48 subject to the restrictions agreed upon */ /* by the DOE and University. */ /* */ /* */ /* DISCLAIMER */ /* */ /* This software was prepared as an account of work sponsored by an */ /* agency of the United States Government. Neither the United States */ /* Government nor the University of California nor any of their */ /* employees, makes any warranty, express or implied, or assumes any */ /* liability or responsibility for the accuracy, completeness, or */ /* usefulness of any information, apparatus, product, or process */ /* disclosed, or represents that its specific commercial products, */ /* process, or service by trade name, trademark, manufacturer, or */ /* otherwise, does not necessarily constitute or imply its */ /* endorsement, recommendation, or favoring by the United States */ /* Government or the University of California. The views and opinions */ /* of the authors expressed herein do not necessarily state or reflect */ /* those of the United States Government or the University of */ /* California, and shall not be used for advertising or product */ /* endorsement purposes. */ /* */ /* Permission to use, copy, modify and distribute this software and its */ /* documentation for any non-commercial purpose, without fee, is */ /* hereby granted, provided that the above copyright notice and this */ /* permission notice appear in all copies of the software and */ /* supporting documentation, and that all UC LLNL identification in */ /* the user interface remain unchanged. The title to copyright LLNL */ /* XDIR shall at all times remain with The Regents of the University */ /* of California and users agree to preserve same. Users seeking the */ /* right to make derivative works with LLNL XDIR for commercial */ /* purposes may obtain a license from the Lawrence Livermore National */ /* Laboratory's Technology Transfer Office, P.O. Box 808, L-795, */ /* Livermore, CA 94550. */ /* */ /***************************************************************************/ /***************************************************************************/ #include #include #include "xdir.h" #include "resources.h" #include "list.h" #include "history.h" struct dirwin_st *dirwin_head = NULL; struct dirwin_st *create_dir_window(); char *entry_to_rel_path(); char *links_to_path(); char **path_to_links(); struct entry_info *rel_path_to_entry(); void set_first_visible_entry(); extern struct st_host_info hinfo[]; extern Display *display; extern int initial_layout; extern AppData app_data; extern int initial_tunneling_mode; extern int initial_dotfiles_mode; extern int initial_cache_mode; /* * display_dir - Display the directory specified by "path" for the * host specified by "host". If the directory is already * being displayed, pop its window to the top. If the * directory is not already displayed and if "dirwin" is * is non-null then display the directory in the window * specified by "dirwin". If the directory is not already * displayed and if "dirwin" is null, then create a new * directory window to display the directory in. Set * "refresh_existing" to True if you want a directory * window that already displays "dir" to be refreshed. * Set "raise" to True is you want a directory window * that already displays "dir" to be raised. Note that * "refresh_existing" and "raise" are independent. Set * "cfetch_ok" to True if it is ok to fetch the directory * list from the cache. Set "cstore_ok" to True if it is * ok to use the directory list to update the cache. Returns * 0 if successful, -3 for lost connection, -6 for stop * button pushed, and -1 for other errors. */ display_dir(host, dirwin, path, refresh_existing, raise, cstore_ok, cfetch_ok) int host; struct dirwin_st *dirwin; char *path; int refresh_existing; int raise; int cstore_ok; int cfetch_ok; { struct dirwin_st *temp_dirwin; char *true_path; int already_displayed = False; struct sl_struct *dlist; int retval; int i; int width; int layout; int tunneling_mode; int dotfiles_mode; int cache_mode; struct entry_info *einfo; int need_to_create; char *temp; char *username; char *anchor_rel_path = NULL; int indx; int used_cache = False; /* This could take some time */ use_busy_cursor(); /* Determine true path of directory */ retval = determine_true_path(host, path, True, cstore_ok, cfetch_ok, &true_path); if (retval < 0) { restore_prev_cursor(); return retval; } /* Is directory already being displayed? */ if (is_dir_displayed(host, true_path, &temp_dirwin, &einfo)) { if (einfo) collapse_subtree(&einfo, True); else { dirwin = temp_dirwin; already_displayed = True; if (raise) XMapRaised(display, XtWindow(dirwin->w_shell)); if (refresh_existing) { if (dirwin->layout == TREE) { retval = re_expand_tree(dirwin, cstore_ok, cfetch_ok); XtFree(true_path); restore_prev_cursor(); switch (retval) { case 1: report_cache_use(dirwin, True); return 0; case 0: report_cache_use(dirwin, False); default: return retval; } } } else { XtFree(true_path); restore_prev_cursor(); return 0; } } } /* Will a new dialog be needed to display the directory? */ if (already_displayed || (dirwin && (dirwin->host == host) && dirwin->tunneling_mode)) { need_to_create = False; layout = dirwin->layout; dotfiles_mode = dirwin->dotfiles_mode; if (raise) XMapRaised(display, XtWindow(dirwin->w_shell)); } else { need_to_create = True; if (dirwin) { tunneling_mode = dirwin->tunneling_mode; layout = dirwin->layout; dotfiles_mode = dirwin->dotfiles_mode; cache_mode = dirwin->cache_mode; } else { tunneling_mode = initial_tunneling_mode; layout = initial_layout; dotfiles_mode = initial_dotfiles_mode; cache_mode = initial_cache_mode; } } /* Get directory list */ retval = get_dirlist(host, true_path, layout, dotfiles_mode, cstore_ok, cfetch_ok, &dlist); if (retval < 0) { XtFree(true_path); restore_prev_cursor(); return retval; } if (retval == 1) used_cache = True; /* Remember topmost visible entry */ if (already_displayed && refresh_existing && dirwin->nentries) { indx = dirwin->first_visible_row; if (indx) anchor_rel_path = entry_to_rel_path(&dirwin->entries[indx]); } /* Create dir window and inherit modes from "parent" dir window */ if (need_to_create) { dirwin = create_dir_window(); dirwin->host = host; dirwin->layout = layout; dirwin->tunneling_mode = tunneling_mode; dirwin->dotfiles_mode = dotfiles_mode; dirwin->cache_mode = cache_mode; update_goto_menu(dirwin); update_launch_menus(dirwin); update_xfer_mode_menu(dirwin); update_viewed_files_menu(dirwin); if (host == LOCAL) XtSetSensitive(dirwin->w_quotedItem, False); } /* Update directory name */ XtFree(dirwin->dirname); dirwin->dirname = XtNewString(true_path); /* Update title bar */ if (host == LOCAL) username = XtNewString("Local"); else username = XtNewString(hinfo[dirwin->host].username); temp = XtMalloc(strlen(hinfo[dirwin->host].hostname)+strlen(username) +strlen(dirwin->dirname)+8); sprintf(temp, "%s (%s) %s", hinfo[dirwin->host].hostname, username, dirwin->dirname); XtFree(username); XtVaSetValues(dirwin->w_shell, XmNtitle, temp, NULL); XtFree(temp); /* Update the "Go to Ancestors" menus */ update_ancestors_menus(dirwin); /* Allocate space to hold directory entry info */ for (i=0; inentries; i++) { XtFree(dirwin->entries[i].name); XtFree(dirwin->entries[i].info); } if (dirwin->nentries) XtFree((char *)dirwin->entries); if (dlist->nentries) dirwin->entries = (struct entry_info *)XtMalloc(dlist->nentries* sizeof(struct entry_info)); dirwin->nentries = dlist->nentries; /* Save information about directory entries */ dirwin->max_entry_width = 0; for (i=0; inentries; i++) { einfo = &dirwin->entries[i]; switch (dirwin->layout) { case TABULAR: einfo->name = XtNewString(dlist->entries[i]); einfo->type = UNKNOWN_TYPE; einfo->info = NULL; width = XTextWidth(app_data.variable_width_layout_font, einfo->name, strlen(einfo->name))+2*TMARGIN; einfo->width = width; break; case ICONIC: case TREE: parse_short_entry(hinfo[host].system, dlist->entries[i], &einfo->name, &einfo->type); einfo->info = NULL; width = XTextWidth(app_data.variable_width_layout_font, einfo->name, strlen(einfo->name))+2*TMARGIN+IWIDTH+IMARGIN; einfo->width = width; break; case FULL_INFO: parse_long_entry(hinfo[host].system, hinfo[host].server, dlist->entries[i], &einfo->name, &einfo->info, &einfo->type); width = XTextWidth(app_data.fixed_width_layout_font, einfo->info, strlen(einfo->info))+2*TMARGIN; einfo->width = width; break; } einfo->state = UNSELECTED; einfo->indx = i; einfo->level = 0; einfo->dirwin = dirwin; einfo->expanded = False; dirwin->max_entry_width = MAX(dirwin->max_entry_width, width); } dirwin->entry_height = app_data.variable_width_layout_font->ascent+ app_data.variable_width_layout_font->descent; switch (dirwin->layout) { case TABULAR: case FULL_INFO: dirwin->entry_height += 2*TMARGIN; break; case ICONIC: case TREE: dirwin->entry_height = MAX(dirwin->entry_height, IHEIGHT)+2*TMARGIN; } /* Update cache of referenced directories */ add_to_history(DIRECTORY, hinfo[host].hostname, true_path); update_goto_menus_for_host(host); /* Perform geometry calculations for directory display */ calc_dirwin_geometry(dirwin); /* Redraw changed part of directory display */ if (need_to_create) reset_scrollbars(dirwin, 0, 0); else { if (anchor_rel_path) { set_first_visible_entry(dirwin, anchor_rel_path); XtFree(anchor_rel_path); } else { reset_scrollbars(dirwin, 0, 0); redraw_entire_dir(dirwin); } } /* Free up memory */ XtFree(true_path); release_array_list(dlist); update_dir_controls(); update_wins_menu(); restore_prev_cursor(); report_cache_use(dirwin, used_cache); return 0; } /* * close_directory_window - Removes the specified directory window and its * associated data structure. */ void close_directory_window(dirwin) struct dirwin_st *dirwin; { struct dirwin_st *dwin; int i; int host = dirwin->host; /* If stop button is showing, get rid of it */ if (stop_button_is_showing(dirwin)) hide_stop_button(); /* Remove dialog from cursor linked list */ remove_dialog_from_list(dirwin->w_shell); /* Destroy window */ XtDestroyWidget(dirwin->w_shell); /* Release associated memory */ for (i=0; inentries; i++) { XtFree(dirwin->entries[i].name); XtFree(dirwin->entries[i].info); } if (dirwin->nentries) XtFree((char *)dirwin->entries); XtFree(dirwin->dirname); /* Disconnect from linked list */ if (dirwin->prev) dirwin->prev->next = dirwin->next; else dirwin_head = dirwin->next; if (dirwin->next) dirwin->next->prev = dirwin->prev; XtFree((char *)dirwin); /* Clean up if this is last directory window for remote host */ dwin = dirwin_head; while (dwin && (dwin->host != host)) dwin = dwin->next; if ((dwin == NULL) && (host != LOCAL)) { close(hinfo[host].ctrl_fd); release_host(host); } /* Update directory window controls */ update_wins_menu(); update_dir_controls(); } void clear_selected_entries() { struct dirwin_st *dirwin; int i; dirwin = dirwin_head; while(dirwin) { if (dirwin->has_selection) { for (i=0; inentries; i++) deselect_entry(&dirwin->entries[i]); dirwin->has_selection = False; return; } dirwin = dirwin->next; } } /* * has_selected_entries - Returns true if dirwin contains selected * entries, else False. */ has_selected_entries(dirwin) struct dirwin_st *dirwin; { int i; for (i=0; inentries; i++) if (dirwin->entries[i].state == SELECTED) return True; return False; } /* * cb_refresh_dir - Callback to refresh a directory display. */ void cb_refresh_dir(widget, client_data, call_data) Widget widget; XtPointer client_data; XtPointer call_data; { struct dirwin_st *dirwin = (struct dirwin_st *)client_data; int retval; /* Start operation */ if (!start_op(False)) return; /* Clear error flag */ raise_okflag(); /* Make operation interruptable for remote hosts */ if (dirwin->host != LOCAL) show_stop_button(dirwin); /* Make sure remote connection is good */ if (dirwin->host != LOCAL) { retval = check_connection(dirwin->host, dirwin); if (retval < 0) { switch (retval) { case -6: record_abort("Refresh Directory Window"); break; case -1: record_and_alert("Unable to refresh directory.", dirwin->w_shell); } hide_stop_button(); end_op(); return; } } /* Can assume directory is already displayed */ retval = display_dir(dirwin->host, dirwin, dirwin->dirname, True, False, dirwin->cache_mode, False); switch (retval) { case -6: record_abort("Refresh Directory Window"); break; case -3: restore_lost_connection(dirwin->host, dirwin); break; case -1: record_and_alert("Unable to refresh directory.", dirwin->w_shell); } if (dirwin->host != LOCAL) hide_stop_button(); /* End operation */ end_op(); } /* * is_dir_displayed - Returns True if indicated directory is displayed, * else False. If directory is found, "dirwin" is set * point to the directory window structure. If the * directory is being displayed as a lower-level * subdirectory, "einfo", a pointer to the entry * structure for that directory is also returned * (otherwise einfo is set to NULL). "path" must be * the full true path name (e.g., "/a/b/c"). */ is_dir_displayed(host, path, dirwin, einfo) int host; char *path; struct dirwin_st **dirwin; struct entry_info **einfo; { struct dirwin_st *dwin; struct entry_info *ret_einfo; int nlinks; char **links; char **parent_links; char **rel_path_links; char **ptr; char *parent_dir; char *rel_path; int system = hinfo[host].system; int i; int j; /* Does directory have its own display? */ dwin = dirwin_head; while (dwin) { if (dwin->host == host && strcmp(dwin->dirname, path) == 0) { *dirwin = dwin; *einfo = NULL; return True; } dwin = dwin->next; } /* Is an ancestor being displayed? Find the closest one. */ links = path_to_links(system, path); nlinks = 0; ptr = links; while (*ptr++) nlinks++; for (i=nlinks-1; i>0; i--) { parent_links = (char **)XtMalloc(sizeof(char *)*(i+1)); for (j=0; jhost == host && !strcmp(dwin->dirname, parent_dir)) { XtFree(parent_dir); goto found_ancestor; } dwin = dwin->next; } XtFree(parent_dir); } release_path_links(links); return False; found_ancestor: /* Is directory being displayed as subdirectory of ancestor? */ if (dwin->layout != TREE) { release_path_links(links); return False; } rel_path_links = (char **)XtMalloc(sizeof(char *)*(nlinks-i+1)); for (j=0; jexpanded) { *dirwin = dwin; *einfo = ret_einfo; return True; } else return False; } /* * rel_path_to_entry - Return a pointer to the info for the entry * specifed by "path", a partial directory path. * Returns NULL if entry not found. */ struct entry_info * rel_path_to_entry(dirwin, path) struct dirwin_st *dirwin; char *path; { char **links; char **link_ptr; char *ptr; char *entry; int i; int level = 0; int system = hinfo[dirwin->host].system; if (dirwin->nentries == 0) return NULL; links = path_to_links(system, path); link_ptr = links; if (*link_ptr == NULL) fatal_error("Bug in rel_path_to_entry()"); for (i=0; inentries; i++) { if (dirwin->entries[i].level < level) break; entry = XtNewString(dirwin->entries[i].name); if ((system == SYS_VMS) && (ptr = strstr(entry, ".dir;"))) *(ptr+4) = '\0'; if ((dirwin->entries[i].level == level) && !strcmp(entry, *link_ptr)) { level++; link_ptr++; if (*link_ptr == NULL) { XtFree(entry); release_path_links(links); return &dirwin->entries[i]; } } XtFree(entry); } release_path_links(links); return NULL; } /* * rel_path_to_nearest_entry - Return a pointer to the info for the entry * specifed by "path", a partial directory path. * If no such entry exists, return a pointer to * the info for the entry nearest to where the * entry for "path" would be. Returns 0 if * there are no entries in the dirwin. */ struct entry_info * rel_path_to_nearest_entry(dirwin, path) struct dirwin_st *dirwin; char *path; { char *path_copy; char *link; int i; int level = 0; int retval; if (dirwin->nentries == 0) return NULL; path_copy = XtNewString(path); if ((link = strtok(path_copy, "/")) == NULL) fatal_error("Bug in rel_path_to_entry()"); for (i=0; inentries; i++) { if (dirwin->entries[i].level < level) break; if (dirwin->entries[i].level == level) { retval = strcmp(dirwin->entries[i].name, link); if (retval == 0) { level++; if ((link = strtok(NULL, "/")) == NULL) break; } else if (retval > 0) break; } } XtFree(path_copy); i = MIN(i, dirwin->nentries-1); return &dirwin->entries[i]; } /* * set_first_visible_entry - In the specified directory window, position the * entry whose relative path is closest to the one * given on the first visible row. */ void set_first_visible_entry(dirwin, rel_path) struct dirwin_st *dirwin; char *rel_path; { struct entry_info *einfo; int row; int first_visible_row; /* Determine first visible row */ if ((einfo = rel_path_to_nearest_entry(dirwin, rel_path))) { row = einfo->indx%dirwin->nrows; first_visible_row = MIN(row, MAX(0, dirwin->nentries-dirwin->nrows_visible)); } else first_visible_row = 0; /* Fix up scrollbars */ reset_scrollbars(dirwin, -1, first_visible_row); /* Redraw visible part of directory display */ redraw_entire_dir(dirwin); }