/***************************************************************************/ /***************************************************************************/ /* */ /* (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 "xdir.h" #include "list.h" #define BYTES_PER_KB 1024 #define DIRLIST 0 #define TRUEPATH 1 struct hostuser_st { struct hostuser_st *next; char *hostname; char *username; }; struct dirlist_part { int layout; int dotfiles; int count; char *list; }; struct mapping_part { char *true_path; }; struct cache_link { struct cache_link *prev; struct cache_link *next; struct hostuser_st *hostuser; char *path; long len; int type; union { struct dirlist_part d; struct mapping_part m; } part; }; static struct hostuser_st *head_hostuser = NULL; static struct cache_link *head = NULL; static struct cache_link *tail = NULL; static long cache_len = 0; extern int max_dir_cache_len; extern struct st_host_info hinfo[]; extern int diagnostics; void free_cache_entry(); void report_cache_addition(); void report_cache_deletion(); void report_cache_truepath_hit(); void report_cache_dirlist_hit(); void check_max_cache_len(); /* * insert_at_cache_head - Insert the cache entry pointed by "ptr" at the * head of the cache. */ void insert_at_cache_head(ptr) struct cache_link *ptr; { ptr->next = head; ptr->prev = NULL; if (head) head->prev = ptr; head = ptr; if (!tail) tail = ptr; } /* * make_room_in_cache - Free up the least recently used cache entries * to create enough space for a directory list * "len" bytes in length. Returns True if successful, * else False. */ make_room_in_cache(len) long len; { long bytes_available = max_dir_cache_len*BYTES_PER_KB-cache_len; /* No hope if entry is bigger than entire cache */ if (len > max_dir_cache_len*BYTES_PER_KB) return False; /* Delete least recently used entries until there is enough room */ while (head && (bytes_availablelen; free_cache_entry(tail); } /* Sanity check */ if (len > bytes_available) fatal_error("Bug in make_room_in_cache()"); /* Looks good */ return True; } /* * free_cache_entry - Free the cache entry pointed to by "ptr". This * function is a no-op if "ptr" is NULL. */ void free_cache_entry(ptr) struct cache_link *ptr; { /* No-op if pointer is NULL */ if (!ptr) return; /* Debug edit */ report_cache_deletion(ptr); /* Adjust cache len */ cache_len -= ptr->len; /* Free the entry's fields */ XtFree(ptr->path); switch (ptr->type) { case DIRLIST: XtFree(ptr->part.d.list); break; case TRUEPATH: XtFree(ptr->part.m.true_path); } /* Now remove the entry from the doubly-linked list */ if (ptr->prev) ptr->prev->next = ptr->next; else head = ptr->next; if (ptr->next) ptr->next->prev = ptr->prev; else tail = ptr->prev; XtFree((char *)ptr); } /* * find_dirlist_in_cache - Return a pointer to the link in the cache * that contains the directory list that is * uniquely specified by the arguments. If * there is no hit, return NULL. */ struct cache_link * find_dirlist_in_cache(host, path, layout, dotfiles) int host; char *path; int layout; int dotfiles; { struct hostuser_st *hostuser = head_hostuser; struct cache_link *ptr = head; /* Find hostname/username pair */ while (hostuser) { if (!strcmp(hinfo[host].hostname, hostuser->hostname) && (!strcmp(hinfo[host].username, hostuser->username))) break; hostuser = hostuser->next; } if (hostuser == NULL) return NULL; /* TREE layout is the same as ICONIC */ if (layout == TREE) layout = ICONIC; /* Search the cache for a match */ while (ptr) { if ((ptr->type == DIRLIST) && (hostuser == ptr->hostuser) && !strcmp(path, ptr->path) && (layout == ptr->part.d.layout) && (dotfiles == ptr->part.d.dotfiles)) return ptr; ptr = ptr->next; } return NULL; } /* * find_path_in_cache - Return a pointer to the link in the cache * that contains the directory mapping that is * uniquely specified by the arguments. If * there is no hit, return NULL. */ struct cache_link * find_path_in_cache(host, path, type) int host; char *path; int type; { struct hostuser_st *hostuser = head_hostuser; struct cache_link *ptr = head; /* Find hostname/username pair */ while (hostuser) { if (!strcmp(hinfo[host].hostname, hostuser->hostname) && (!strcmp(hinfo[host].username, hostuser->username))) break; hostuser = hostuser->next; } if (hostuser == NULL) return NULL; /* Search the cache for an exact match */ while (ptr) { if ((ptr->type == type) && (ptr->hostuser == hostuser) && !strcmp(ptr->path, path)) return ptr; ptr = ptr->next; } return NULL; } /* * find_truepath_in_cache - Return a pointer to the link in the cache * that contains the specified path in the * "true_path" field. If there is no hit, * return NULL. */ struct cache_link * find_truepath_in_cache(host, path) int host; char *path; { struct hostuser_st *hostuser = head_hostuser; struct cache_link *ptr = head; /* Find hostname/username pair */ while (hostuser) { if (!strcmp(hinfo[host].hostname, hostuser->hostname) && (!strcmp(hinfo[host].username, hostuser->username))) break; hostuser = hostuser->next; } if (hostuser == NULL) return NULL; /* Search the cache for an exact match */ while (ptr) { if ((ptr->type == TRUEPATH) && (ptr->hostuser == hostuser) && !strcmp(ptr->part.m.true_path, path)) return ptr; ptr = ptr->next; } return NULL; } /* * add_dirlist_to_cache - Add to the cache the directory list "dlist", * which is uniquely specified by the other * arguments. */ void add_dirlist_to_cache(host, path, layout, dotfiles, dlist) int host; char *path; int layout; int dotfiles; struct sl_struct *dlist; { struct hostuser_st *hostuser = head_hostuser; long len; long list_len; int i; int slen; struct cache_link *ptr; char *p; /* Make sure that cache len is consistent with current max len */ check_max_cache_len(); /* Find hostname/username pair */ while (hostuser) { if (!strcmp(hinfo[host].hostname, hostuser->hostname) && (!strcmp(hinfo[host].username, hostuser->username))) break; hostuser = hostuser->next; } if (hostuser == NULL) { hostuser = XtNew(struct hostuser_st); hostuser->hostname = XtNewString(hinfo[host].hostname); hostuser->username = XtNewString(hinfo[host].username); hostuser->next = head_hostuser; head_hostuser = hostuser; } /* If directory is already in the cache, delete it */ free_cache_entry(find_dirlist_in_cache(host, path, layout, dotfiles)); /* Determine the length of the directory list (plus overhead) */ list_len = 0; for (i=0; inentries; i++) list_len += strlen(dlist->entries[i]); list_len += dlist->nentries; len = list_len+strlen(path)+sizeof(struct cache_link); /* Make room for cache entry */ if (!make_room_in_cache(len)) return; /* Create and initialize the cache entry */ ptr = XtNew(struct cache_link); ptr->type = DIRLIST; ptr->hostuser = hostuser; ptr->path = XtNewString(path); if (layout == TREE) ptr->part.d.layout = ICONIC; else ptr->part.d.layout = layout; ptr->part.d.dotfiles = dotfiles; ptr->len = len; ptr->part.d.count = dlist->nentries; ptr->part.d.list = XtMalloc(len); /* Now copy the actual directory entries */ p = ptr->part.d.list; for (i=0; inentries; i++) { slen = strlen(dlist->entries[i]); bcopy(dlist->entries[i], p, slen+1); p += slen+1; } /* Insert the entry at the head of the the cache */ insert_at_cache_head(ptr); cache_len += ptr->len; report_cache_addition(); } /* * add_truepath_to_cache - Add to the cache the directory mapping specified * by the arguments. */ void add_truepath_to_cache(host, path, true_path) int host; char *path; char *true_path; { struct hostuser_st *hostuser = head_hostuser; long len; struct cache_link *ptr; /* Make sure that cache len is consistent with current max len */ check_max_cache_len(); /* Find hostname/username pair */ while (hostuser) { if (!strcmp(hinfo[host].hostname, hostuser->hostname) && (!strcmp(hinfo[host].username, hostuser->username))) break; hostuser = hostuser->next; } if (hostuser == NULL) { hostuser = XtNew(struct hostuser_st); hostuser->hostname = XtNewString(hinfo[host].hostname); hostuser->username = XtNewString(hinfo[host].username); hostuser->next = head_hostuser; head_hostuser = hostuser; } /* If a mapping for "path" is already in the cache, delete it */ ptr = find_path_in_cache(host, path, TRUEPATH); free_cache_entry(ptr); /* Determine the length of the mapping (plus overhead) */ len = strlen(path)+strlen(true_path)+sizeof(struct cache_link); /* Make room for cache entry */ if (!make_room_in_cache(len)) return; /* Create and initialize the cache entry */ ptr = XtNew(struct cache_link); ptr->type = TRUEPATH; ptr->hostuser = hostuser; ptr->path = XtNewString(path); ptr->part.m.true_path = XtNewString(true_path); ptr->len = len; /* Insert the entry at the head of the the cache */ insert_at_cache_head(ptr); cache_len += ptr->len; report_cache_addition(); } /* * fetch_dirlist_from_cache - If the directory list uniquely specified * by the input arguments exists in the cache, * then this function sets "dlist" to a pointer * to a structure that contains that directory * list. Returns True if successful, else False. */ fetch_dirlist_from_cache(host, path, layout, dotfiles, dlist) int host; char *path; int layout; int dotfiles; struct sl_struct **dlist; { struct cache_link *ptr; int i; int slen; char *p; /* Make sure that cache len is consistent with current max len */ check_max_cache_len(); /* Look for directory in cache */ if ((ptr = find_dirlist_in_cache(host, path, layout, dotfiles)) == NULL) return False; /* Move found directory to head of cache */ if (ptr->prev) ptr->prev->next = ptr->next; else head = ptr->next; if (ptr->next) ptr->next->prev = ptr->prev; else tail = ptr->prev; insert_at_cache_head(ptr); /* Allocate list structure and fill it in */ *dlist = XtNew(struct sl_struct); (*dlist)->nentries = ptr->part.d.count; (*dlist)->entries = (char **)XtMalloc(sizeof(char *)*ptr->part.d.count); p = ptr->part.d.list; for (i=0; ipart.d.count; i++) { slen = strlen(p)+1; (*dlist)->entries[i] = XtMalloc(slen); bcopy(p, (*dlist)->entries[i], slen); p += slen; } report_cache_dirlist_hit(); return True; } /* * fetch_truepath_from_cache - If the true path information specified by the * input arguments exists in the cache, then * this function sets "true_path" to a pointer * to a string that contains the true directory * path for "path". Returns True if successful, * else False. */ fetch_truepath_from_cache(host, path, true_path) int host; char *path; char **true_path; { struct cache_link *ptr; /* Make sure that cache len is consistent with current max len */ check_max_cache_len(); /* First look for mapping in cache */ if ((ptr = find_path_in_cache(host, path, TRUEPATH)) == NULL) if ((ptr = find_truepath_in_cache(host, path)) == NULL) return False; /* Move found mapping to head of cache */ if (ptr->prev) ptr->prev->next = ptr->next; else head = ptr->next; if (ptr->next) ptr->next->prev = ptr->prev; else tail = ptr->prev; insert_at_cache_head(ptr); /* Return true path */ *true_path = XtNewString(ptr->part.m.true_path); report_cache_truepath_hit(); return True; } /* * report_cache_use - Let's user know if cache information was used to * update "dirwin". Set "used_cache" to True for * message to be displayed. Set "used_cache" to * False for no message. */ void report_cache_use(dirwin, used_cache) struct dirwin_st *dirwin; int used_cache; { if (used_cache) XtManageChild(dirwin->w_cacheMsg); else XtUnmanageChild(dirwin->w_cacheMsg); } /* * report_cache_addition - Debug edit */ void report_cache_addition() { char msg[100]; if (diagnostics == DEBUG) { sprintf(msg, "*** Entry added to cache. Cache len = %ld\n", cache_len); write_log(msg); } } /* * report_cache_deletion - Debug edit */ void report_cache_deletion(ptr) struct cache_link *ptr; { char msg[200]; if (diagnostics == DEBUG) { if (ptr->type == DIRLIST) { sprintf(msg, "*** Removed directory list from cache. Cache len = %ld\n", cache_len); write_log(msg); sprintf(msg, " %s", ptr->path); write_log(msg); switch (ptr->part.d.layout) { case TABULAR: write_log(", Tabular"); break; case ICONIC: case TREE: write_log(", Iconic/Tree"); break; case FULL_INFO: write_log(", Full Info"); } if (ptr->part.d.dotfiles) write_log(", Dotfiles"); write_log("\n"); } else { sprintf(msg, "*** Removed true path from cache. Cache len = %ld\n", cache_len); write_log(msg); sprintf(msg, " %s\n", ptr->path); write_log(msg); } } } /* * report_cache_truepath_hit - Debug edit */ void report_cache_truepath_hit() { if (diagnostics == DEBUG) write_log("*** True path cache hit\n"); } /* * report_cache_dirlist_hit - Debug edit */ void report_cache_dirlist_hit() { if (diagnostics == DEBUG) write_log("*** Directory list cache hit\n"); } /* * check_max_cache_len - If cache length exceeds the maximum allowable (this * could happen if the user changes this limit), then * free up as many of the least recently used cache * entries as necessary. */ void check_max_cache_len() { long bytes_available = max_dir_cache_len*BYTES_PER_KB-cache_len; /* Delete least recently used entries until cache len is small enough */ while (head && (bytes_available<0)) { bytes_available += tail->len; free_cache_entry(tail); } /* Sanity check */ if (bytes_available < 0) fatal_error("Bug in check_max_cache_len()"); } /* * remove_dirlist_from_cache - Remove the cache entry that contains the * directory list that is uniquely specified * by the arguments. */ void remove_dirlist_from_cache(host, path, layout, dotfiles) int host; char *path; int layout; int dotfiles; { free_cache_entry(find_dirlist_in_cache(host, path, layout, dotfiles)); } /* * flush_cache_all - Removes all entries from the cache. */ void flush_cache_all() { while (head) free_cache_entry(head); } /* * flush_cache_directory - Remove all cache entries for the directory * specified by "host" and "path". */ void flush_cache_directory(host, path) int host; char *path; { struct hostuser_st *hostuser = head_hostuser; struct cache_link *ptr = head; struct cache_link *tptr; /* Find hostname/username pair */ while (hostuser) { if (!strcmp(hinfo[host].hostname, hostuser->hostname) && (!strcmp(hinfo[host].username, hostuser->username))) break; hostuser = hostuser->next; } if (hostuser == NULL) return; /* Now remove cache entries */ while (ptr) { tptr = ptr; ptr = ptr->next; if ((hostuser == tptr->hostuser) && !strcmp(path, tptr->path)) free_cache_entry(tptr); } }