Newsgroups: comp.sources.unix From: sunil@hal.com (Sunil Savkar) Subject: v25i106: setd V1.7 -- Set Directory and Mark Utilities Sender: sources-moderator@pa.dec.com Approved: vixie@pa.dec.com Submitted-By: sunil@hal.com (Sunil Savkar) Posting-Number: Volume 25, Issue 106 Archive-Name: setd setd -- (Set Directory and Mark Utilities version 1.7) Only works in UNIX and tested on Suns, HPs, RS/6000, etcetera sunil@hal.com (Sunil Savkar) #! /bin/sh # This is a shell archive. Remove anything before this line, then feed it # into a shell via "sh file" or similar. To overwrite existing files, # type "sh file -c". # The tool that generated this appeared in the comp.sources.unix newsgroup; # send mail to comp-sources-unix@uunet.uu.net if you want that tool. # If this archive is complete, you will see the following message at the end: # "End of shell archive." # Contents: mark.c setd.c enum.h macros.h types.h mark.1 setd.1 # Makefile README DISCLOSURE # Wrapped by sunil@jaguar on Wed Jan 8 22:14:42 1992 PATH=/bin:/usr/bin:/usr/ucb ; export PATH if test -f 'mark.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'mark.c'\" else echo shar: Extracting \"'mark.c'\" \(14830 characters\) sed "s/^X//" >'mark.c' <<'END_OF_FILE' X/* X * MARK X * Mark Directory X * Version 1.7 X * X * Sunil William Savkar X * sunil@hal.com X * Copyright (c) 1991 X * All Rights Reserved X * X * DISCLOSURE X * X * This source may be modified or copied freely. The intent X * is the free distribution of a useful utility used for moving X * between directories. Any modifications and additions, along X * with bug reports should be sent to the author, so all might X * benefit! X * X * DESCRIPTION X * X * Mark directory utility used in conjunction with the X * setd command to allow ease of directory changes. X * X * MODIFICATION HISTORY X * X * 8/2/91 Sunil William Savkar X * Changed code to allow running on HPs. X * X * 12/6/92 Sunil William Savkar X * Changed code to allow for alphabetical sorting X * of the mark file (easier to peruse). X * X * Please send all updates along with suggestions X * to sunil@hal.com X */ X X#if (MACH == hp || MACH == m88k || MACH == rs) X#include X#else X#include X#endif X#include X#include X#include X#include "macros.h" X#include "enum.h" X#include "types.h" X X X/* X * command[] X * X * This array holds all the strings which are command line arguments X * recognized, along with the enumerated type they match with. X */ X XCOMMAND_NODE command[] = { K_MARK_DIR, "", K_REMOVE_MARK, "-rm", X K_HELP, "-h", K_HELP, "-help", K_VERSION, X "-v", K_VERSION, "-ver", K_VERSION, "version", X K_RESET_MARKS, "-reset", K_REMOVE_MARK, "-remove", X K_LIST_MARKS, "-list", K_REFRESH_MARKS, "-r", X K_LIST_MARKS, "-l", X K_REFRESH_MARKS, "-refresh", K_REFRESH_MARKS, "-ref", X K_NULL, NULL}; X X/* X * version_header[] X */ X Xstatic char version_header[] = X"Mark Directory\tv1.7\tSunil William Savkar\n"; X X/* X * help_header[] X */ X Xstatic char help_header[] = X"Mark Directory\tv1.7\nusage:\tmark \n\n\ Xoption\t\t\tdescription\n\n\ X\n\ X-l\t\t\tLists current marks and their directories\n\ X[mark]\t\t\tAliases current directory to alphanumeric mark name\n\ X-rm [mark]\n\ X-remove [mark]\t\tRemoves specified mark\n\ X-v\t\tPrints current version of the program\n\ X-h\t\t\tThis help message\n\ X-reset\t\t\tClears all marks in the current environment\n\ X-r\t\tRefreshes all marks in the current environment\n\ X\nexamples:\tmark xxx, mark -list, mark -reset, mark -rm xxx\n"; X X/* X * boolean read_into_list( char *mark_fil, X * LIST_NODE **list_head) X * X * This routine opens the passed mark file, holding all the currently X * enabled marks, and parses them into the linked list of marks X * with their expansions. X */ X Xboolean read_into_list(mark_fil, /* The mark file */ X list_head, /* Pointer to the head of the mark list */ X list_tail) Xchar *mark_fil; XLIST_NODE **list_head; XLIST_NODE **list_tail; X{ X X FILE *mark_fp; /* Pointer to the mark file stream */ X LIST_NODE *new_ptr; /* New list element to add to marks */ X char setenv[MAX_LINE]; /* Contains the setenv line in mark file */ X char mark[MAX_LINE]; /* The mark string which will expand */ X char path[MAX_LINE]; /* The expansion of the mark */ X char buffer[MAX_LINE]; /* Temporary buffer to hold information */ X X /* X * Open the mark file. X */ X X *list_tail = *list_head; X if (!(mark_fp = fopen(mark_fil, "r"))) { X X fprintf(stderr, "read_into_list: Unable to open %s\n", mark_fil); X return FALSE; X } X X /* X * Now continue reading the file and setting up the linked list. X */ X X while (fgets(buffer, MAX_LINE, mark_fp)) { X X /* X * Format for the file is : "setenv mark_ " X */ X X sscanf(buffer, "%s mark_%s %s", setenv, mark, path); X X if (STREQU(setenv, "unsetenv")) continue; X if (!STREQU(setenv, "setenv")) { X X fprintf(stderr, "read_into_list: Error in mark database\n"); X return FALSE; X } X X /* X * Set up a new node. X */ X X if (!(new_ptr = (LIST_NODE *)malloc(sizeof(LIST_NODE)))) { X X fprintf( stderr, "read_into_list: error mallocing new structure\n"); X return FALSE; X } X X new_ptr->unset_flag = FALSE; X if (!(new_ptr->mark = (char *)malloc(strlen(mark) + 1))) { X X fprintf( stderr, "read_into_list: error mallocing new mark string\n"); X return FALSE; X } else if (!(new_ptr->path = (char *)malloc(strlen(path) + 1))) { X X fprintf( stderr, "read_into_list: error mallocing new expand string\n"); X return FALSE; X } X new_ptr->next = NULL; X strcpy(new_ptr->mark, mark); strcpy(new_ptr->path, path); X X /* X * Insert the new mark into the list. X */ X X if (*list_head == NULL) { X X *list_head = *list_tail = new_ptr; X } else { X X (*list_tail)->next = new_ptr; X *list_tail = new_ptr; X } X } X X /* X * Done with generating the linked list of current marks. X */ X X fclose(mark_fp); X return TRUE; X} X X/* X * boolean initialize( char **pwd, X * char *mark_fil, X * LIST_NODE **list_head, X * LIST_NODE **list_tail) X * X * This routine is called to initialize the variables and list X * of marks currently set. X */ X Xboolean initialize( pwd, X mark_fil, X list_head, X list_tail) XLIST_NODE **list_tail; XLIST_NODE **list_head; Xchar *mark_fil; Xchar **pwd; X{ X X FILE *mark_fp; X char *getenv(); X char *ptr; X X /* X * Get current directory and pointer to the mark directory. X */ X X#ifdef HP X X if (!(getcwd((*pwd = (char *)malloc(MAX_LINE)), MAX_LINE))) { X#else X X if (!(*pwd = getenv("PWD"))) { X#endif X X fprintf(stderr, "initialize: Unable to get environment var $PWD\n"); X return FALSE; X } X X /* X * Notice we would like to strip off the /tmp_mount from the *pwd, X * if it exists! X */ X X if (strncmp(*pwd, "/tmp_mnt", 8) == 0) { X *pwd += 8; X } X X if (!(ptr = getenv("MARK_DIR"))) { X X fprintf(stderr, "initialize: Must set environment var $MARK_DIR\n"); X return FALSE; X } else { X X /* X * Construct mark data base file pointer. X */ X X sprintf( mark_fil, "%s/mark_db", ptr); X } X X if (!(mark_fp = fopen(mark_fil, "a"))) { X X fprintf(stderr, "initialize: Unable to open %s\n", mark_fil); X return FALSE; X } X X fclose(mark_fp); X X /* X * Read marks into a linked list, with the mark string, X * and the path string. X */ X X if (!read_into_list(mark_fil, list_head, list_tail)) { X X fprintf(stderr, "initialize: Unable to read mark file %s\n", mark_fil); X return FALSE; X } X X return TRUE; X} X X/* X * T_COMM parse( char *option) X * X * This routine returns the enumeration for the given string which X * represents an option on the command line. Options can be anything X * from -help for help, to the mark to be set. X */ X XT_COMM parse( option) /* The string representing the option requested */ Xchar *option; X{ X X int j; X char *ptr; X X /* X * No dash indicates no real option, but just a directory to mark. X */ X X if (*option != '-') { X X return K_MARK_DIR; X } X X /* X * Must be one of the commands, else there is X * an error. X */ X X for (j = 0; ptr = command[j].text; j++) { X X if (STREQU(ptr, option)) break; X } X X if (!ptr) { X X fprintf(stderr, "parse: unrecognized option from main (%s)\n", option); X return K_NULL; X } X X return command[j].type; X} X Xboolean list_marks( list_ptr) XLIST_NODE *list_ptr; X{ X X X /* X * Each line consists of the mark, and then the pathname. X */ X X fprintf(stdout, "MARK\t\tPATH\n----\t\t----\n"); X for (; list_ptr; list_ptr = list_ptr->next) { X X fprintf(stdout, "%s\t\t%s\n", list_ptr->mark, list_ptr->path); X } X X return TRUE; X} X X/* X * boolean mark_dir( char *pwd, X * char *mark, X * LIST_NODE **list_head, X * LIST_NODE **list_tail) X * X * This routine adds a new mark with expansion to the given list. X */ X Xboolean mark_dir(pwd, X mark, X list_head, X list_tail) XLIST_NODE **list_head, X **list_tail; Xchar *pwd; Xchar *mark; X{ X X char *ptr; X LIST_NODE *new_ptr, *list_ptr; X X /* X * A mark must be alphanumeric. X */ X X for (ptr = mark; *ptr != '\0'; ptr++) { X X if (!(isalnum(*ptr) || (*ptr == '_'))) { X X fprintf(stderr, "mark_dir: mark must be alphanumeric\n"); X return FALSE; X } X } X X /* X * If there is an old mark by the same name, just change the X * path for that mark. X */ X X for (list_ptr = *list_head; list_ptr; list_ptr = list_ptr->next) { X X if (STREQU(mark, list_ptr->mark)) break; X } X X if (list_ptr) { X X free(list_ptr->path); X list_ptr->path = pwd; X } else if (!(new_ptr = (LIST_NODE *)malloc(sizeof(LIST_NODE)))) { X X fprintf(stderr, "mark_dir: error allocating space in list\n"); X return FALSE; X } else { X X new_ptr->unset_flag = FALSE; X new_ptr->next = NULL; X new_ptr->mark = mark; X new_ptr->path = pwd; X X if (*list_head == NULL) { X X *list_head = *list_tail = new_ptr; X } else { X X (*list_tail)->next = new_ptr; X *list_tail = new_ptr; X } X } X X fprintf(stderr, "mark_dir: mark \"%s\" (%s) set\n", mark, pwd); X return TRUE; X} X X/* X * boolean remove_mark( char *mark, X * LIST_NODE *list_ptr) X * X * This routine is used to remove a mark from the given list (through X * including an unsetenv declarator in the mark file. X */ X Xboolean remove_mark(mark, X list_ptr) Xchar *mark; XLIST_NODE *list_ptr; X{ X X for (; list_ptr; list_ptr = list_ptr->next) { X X if (STREQU(list_ptr->mark, mark)) { X X list_ptr->unset_flag = TRUE; X fprintf( stderr, "mark_dir: mark \"%s\" (%s) removed\n", mark, X list_ptr->path); X return TRUE; X } X } X X fprintf(stderr, "mark_dir: mark \"%s\" not found\n", mark); X return FALSE; X} X X/* X * boolean reset_marks( LIST_NODE *list_ptr) X * X * This routine resets the marks by unsetenv all of them. X */ X Xboolean reset_marks( list_ptr) XLIST_NODE *list_ptr; X{ X X /* X * Go through all the marks and set their unset_flag. X */ X X for (; list_ptr; list_ptr = list_ptr->next) list_ptr->unset_flag = TRUE; X X return TRUE; X} X X/* X * boolean sort_list( list_head) X * X * This routine simply pops off the old list and rearranges into a sorted X * order. This simple routine is written instead of using the system X * sort command. X */ X Xvoid sort_list( list_head) XLIST_NODE **list_head; X{ X X LIST_NODE *sort_head, X *sort_tail, X *list_ptr, X *old_ptr, X *sort_ptr; X X sort_head = old_ptr = NULL; X X /* X * Pop off the old list and rearrange in the new sort list. X */ X X while (*list_head) { X X list_ptr = *list_head; X *list_head = (*list_head)->next; X list_ptr->next = NULL; X X /* X * No sorted list exists. X */ X X if (!sort_head) { X X sort_head = sort_tail = list_ptr; X continue; X } else { X X /* X * Check for mark precedence over current list head. X */ X X if (strcmp( list_ptr->mark, sort_head->mark) < 0) { X X list_ptr->next = sort_head; X sort_head = list_ptr; X X /* X * Check for mark being of lesser precedence than the tail. X */ X X } else if (strcmp( list_ptr->mark, sort_tail->mark) > 0) { X X sort_tail->next = list_ptr; X sort_tail = list_ptr; X } else { X X /* X * Insert the mark somewhere within the list alphabetically. X */ X X for (sort_ptr = sort_head; sort_ptr; old_ptr = sort_ptr, sort_ptr = sort_ptr->next) { X X if (strcmp( list_ptr->mark, sort_ptr->mark) < 0) { X X old_ptr->next = list_ptr; X list_ptr->next = sort_ptr; X break; X } X } X } X } X } X X /* X * Set the new list head. X */ X X *list_head = sort_head; X} X Xboolean update_file( mark_fil, list_ptr) Xchar *mark_fil; XLIST_NODE *list_ptr; X{ X X FILE *mark_fp; X X /* X * Write the new list of marks to the update file. X */ X X if (!(mark_fp = fopen(mark_fil, "w"))) { X X fprintf(stderr, "update_file: Unable to update %s\n", mark_fil); X return FALSE; X } X X for (; list_ptr; list_ptr = list_ptr->next) { X X if (!list_ptr->unset_flag) { X X fprintf(mark_fp, "setenv mark_%s %s\n", list_ptr->mark, list_ptr->path); X } else { X X fprintf(mark_fp, "unsetenv mark_%s\n", list_ptr->mark); X } X } X X fclose(mark_fp); X return TRUE; X} X X/* X * main( int argc, X * char *argv[]) X * X * The main event. X */ X Xmain(argc, X argv) Xchar *argv[]; Xint argc; X{ X X LIST_NODE *list_head, X *list_tail; X int i; X char mark_fil[MAX_LINE]; X char *pwd; X T_COMM option; X X list_head = list_tail = NULL; X X /* X * First we must initialize, getting pointer to the X * mark file, and read in the current directory. X */ X X if (!initialize(&pwd, mark_fil, &list_head, &list_tail)) { X X fprintf(stderr, "mark: error code from initialize\n"); X exit(-1); X } X X /* X * Now we have the file pointer for mark, and the current X * directory. It is time to start parsing the arguments X * on the command line. X */ X X for (i = 1; i <= argc; i++) { X X if (argc == 1) { X X option = K_LIST_MARKS; X } else if (i == argc) { X X break; X } else if ((option = parse( argv[i])) == K_NULL) { X X X fprintf(stderr, "mark: error code from parse\n"); X exit(-1); X } X X /* X * Switch, based upon the given option, to the X * proper function. X */ X X switch (option) { X X case K_MARK_DIR: X X if (!mark_dir(pwd, argv[i], &list_head, &list_tail)) { X X fprintf(stderr, "mark: error code from mark_dir\n"); X exit(-1); X } X break; X X case K_REMOVE_MARK: X if (!remove_mark(argv[++i], list_head)) { X X fprintf(stderr, "mark: error code from remove_mark\n"); X exit(-1); X } X break; X X case K_LIST_MARKS: X if (!list_marks(list_head)) { X X fprintf(stderr, "mark: error code from list_marks\n"); X exit(-1); X } X break; X X case K_VERSION: X fprintf(stdout, version_header); X break; X X case K_HELP: X fprintf(stdout, help_header); X break; X X case K_RESET_MARKS: X if (!reset_marks(list_head)) { X X fprintf(stderr, "mark: error code from reset_marks\n"); X exit(-1); X } X break; X X case K_REFRESH_MARKS: break; X default: break; X } X } X X if (option == K_MARK_DIR) sort_list( &list_head); X if (!update_file(mark_fil, list_head)) { X X fprintf(stderr, "mark: error code from update_file\n"); X exit(-1); X } X X exit(0); X} END_OF_FILE if test 14830 -ne `wc -c <'mark.c'`; then echo shar: \"'mark.c'\" unpacked with wrong size! fi # end of 'mark.c' fi if test -f 'setd.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'setd.c'\" else echo shar: Extracting \"'setd.c'\" \(15273 characters\) sed "s/^X//" >'setd.c' <<'END_OF_FILE' X/* X * SETD X * Set Directory X * Version 1.7 X * X * Sunil William Savkar X * sunil@hal.com X * Copyright (c) 1991 X * All Rights Reserved X * X * DISCLOSURE X * X * This source may be modified or copied freely. The intent X * is the free distribution of a useful utility used for moving X * between directories. Any modifications and additions, along X * with bug reports should be sent to the author, so all might X * benefit! X * X * DESCRIPTION X * X * Set directory utility used in conjunction with the X * mark command to allow ease of directory changes. X * X * MODIFICATION HISTORY X * X * 8/2/91 Hunter Scales X * Changed code to allow running on HPs. X * 8/20/91 Sunil William Savkar X * Allow fall through of illegal directory X * change so cd can try to use cdpath variable. X * 12/6/92 Sunil William Savkar X * Added changes to allow a mark as a base of a X * full pathname, thus not needing multiple X * marks for subdirectories under a common point. X * X * Please send all updates along with suggestions X * to sunil@hal.com X */ X X#if (MACH == hp || MACH == m88k || MACH == rs) X#include X#else X#include X#endif X#include X#include X#include X#include "macros.h" X#include "enum.h" X#include "types.h" X X/* X * Global Variable Definitions X */ X XCOMMAND_NODE command[] = { X X K_SETD_DIR, "", X K_SETD_HOME, "", X K_POS_QUEUE, "", X K_BACK_QUEUE, "", X K_LIST_QUEUE, "-list", X K_LIST_QUEUE, "-l", X K_MAX_QUEUE, "-max", X K_MAX_QUEUE, "-m", X K_HELP, "-h", X K_HELP, "-help", X K_VERSION, "-v", X K_VERSION, "-ver", X K_VERSION, "-version", X K_NULL, NULL X}; X Xstatic char version_header[] = "Set Directory\tv1.7\tSunil William Savkar\n"; X Xstatic char help_header[] = X"Set Directory\tv1.7\nusage:\tcd \n\n\ Xoption\t\tdescription\n\n\ X[path]\t\tAttempts change to specified directory pathname\n\ X[mark]\t\tAttempts change to directory specified by the mark alias\n\ X[mark]/[path]\tAttempts change to base mark plus appended pathname\n\ X[env]\t\tAttempts change to directory spec'd by environment variable\n\ X%%[path]\t\tAttempts change to subdirectory pathname of root one above\n\ X-l\t\tLists previous directories up to maximum set list length\n\ X-m\t\tSets the maximum depth of the past directory list\n\ Xnumeric\t\tChanges directory to specified list pos, or offset from top (-)\n\ X\nexamples:\tcd ~savkar, cd %bin, cd -4, cd MARK_NAME, cd MARK_NAME/xxx\n"; X X/* X * boolean conv_to_dec( char *string, X * int *conv_dec) X * X * conv_to_dec translates the string to a decimal X * value, and returns this decimal value in conv_dec. X * If an error occurs during conversion, the function X * returns false, else it returns true. X */ X Xboolean conv_to_dec(string, X conv_dec) Xchar *string; /* The string to convert */ Xint *conv_dec; /* The variable to store the converted integer */ X{ X X int num = 0, /* Used to analyze and store shifted value */ X muln = 1; /* +/- multiplier factor */ X X /* X * Check for the sign of the passed string X */ X X if (*string == '-') { X X if (*(++string) == '\0') return FALSE; X muln = -1; X } else if (*string == '+') { X X if (*(++string) == '\0') return FALSE; X } X X /* X * Increment through the string and put together the base ten X * number which is represented. X */ X X for (; *string != '\0'; string++) { X X if (!isdigit(*string)) return FALSE; X num = num*10 + (*string - '0'); X } X X *conv_dec = num*muln; X return TRUE; X} X X/* X * void push_list( LIST_NODE **list, X * LIST_NODE *element, X * int *length) X * X * Push new node onto the list. X */ X Xvoid push_list(list, element, length) XLIST_NODE *element; XLIST_NODE **list; Xint *length; X{ X X element->next = *list; X *list = element; X (*length)++; X} X X/* X * LIST_NODE *shift_list( LIST_NODE *list, X * int *length) X * X * Shift off the last element of the list, and pass X * it to the caller. X */ X XLIST_NODE *shift_list(list, X length) XLIST_NODE **list; Xint *length; X{ X X LIST_NODE *prev, *end; X X if (!(*list)) return NULL; X X if (!(*list)->next) { X X end = *list; X *list = NULL; X } else { X X for (end = *list; end->next; end = end->next) ; X for (prev = *list; prev->next != end; prev = prev->next) ; X prev->next = NULL; X } X X (*length)--; X return (end); X} X X/* X * boolean read_into_list( char *setd_fil, X * LIST_NODE **list_head, X * int *max_queue, X * int *list_length) X * X * read_into_list attempts to open the setd database and read X * the list of prior directory changes into the queue. An error X * is returned if unable to open or read the setd db file. X * X * The database of prior locations is read into a list pointed at X * by *list_head. The length of the list is in the list_length X * variable, and the max_queue variable is also set at this point. X */ X Xboolean read_into_list(setd_fil, list_head, max_queue, list_length) Xchar *setd_fil; XLIST_NODE **list_head; Xint *max_queue; Xint *list_length; X{ X X FILE *setd_fp; X LIST_NODE *new_ptr; X char path[MAX_LINE]; X X *list_length = 0; X X if (!(setd_fp = fopen(setd_fil, "r"))) { X X fprintf(stderr, "read_into_list: Unable to open %s\n", setd_fil); X return FALSE; X } X X /* X * First argument is the maximum queue length. X */ X X *max_queue = 0; X fscanf(setd_fp, "%d", max_queue); X if (!(*max_queue)) { X X *max_queue = 10; X } X X /* X * Now continue reading the file and setting up the linked list. X */ X X while (fscanf(setd_fp, "%s", path) == 1) { X X /* X * Set up a new node. X */ X X new_ptr = (LIST_NODE *)malloc(sizeof(LIST_NODE)); X new_ptr->path = (char *)malloc(strlen(path) + 1); X new_ptr->next = NULL; X strcpy(new_ptr->path, path); X X push_list( list_head, new_ptr, list_length); X } X X fclose(setd_fp); X return TRUE; X} X Xboolean initialize( home, pwd, setd_fil, list_head, max_queue, list_length) XLIST_NODE **list_head; Xchar *setd_fil; Xchar **pwd; Xchar **home; Xint *max_queue, *list_length; X{ X X FILE *setd_fp; X char *getenv(); X char *ptr; X X /* X * Get current directory and pointer to the mark directory. X */ X X#ifdef HP X X if (!(getcwd((*pwd = (char *)malloc(MAX_LINE), MAX_LINE))) { X#else X X if (!(*pwd = getenv("PWD"))) { X#endif X X fprintf(stderr, "initialize: Unable to get environment var $PWD\n"); X return FALSE; X } X X#ifdef HP X X /* X * Notice we would like to strip off the /tmp_mount from the *pwd, X * if it exists! X */ X X if (strncmp(*pwd, "/tmp_mnt", 8) == 0) { X *pwd += 8; X } X#endif X X if (!(*home = getenv("HOME"))) { X X fprintf(stderr, "initialize: Unable to get environment var $HOME\n"); X return FALSE; X } X X if (!(ptr = getenv("SETD_DIR"))) { X X fprintf(stderr, "initialize: Must set environment var $SETD_DIR\n"); X return FALSE; X } else { X X /* X * Construct setd data base file pointer. X */ X X sprintf( setd_fil, "%s/setd_db", ptr); X } X X if (!(setd_fp = fopen(setd_fil, "a"))) { X X fprintf(stderr, "initialize: Unable to open %s\n", setd_fil); X return FALSE; X } X X fclose(setd_fp); X X /* X * Read queue into a linked list, with the path string. X */ X X if (!read_into_list(setd_fil, list_head, max_queue, list_length)) { X X fprintf(stderr, "initialize: Unable to read setd file %s\n", setd_fil); X return FALSE; X } X X return TRUE; X} X XT_COMM parse( option) Xchar *option; X{ X X int j; X int num; X char *ptr; X X if (*option != '-') { X X return K_SETD_DIR; X } X X /* X * Must be one of the commands, else there is X * an error. X */ X X for (j = 0; (ptr = command[j].text) && !STREQU( ptr, option); j++) ; X X if (!ptr) { X X if (conv_to_dec(option, &num)) return K_SETD_DIR; X fprintf(stderr, "parse: unrecognized option from main (%s)\n", option); X return K_NULL; X } X X return command[j].type; X} X Xboolean list_queue( list_ptr, max_queue) XLIST_NODE *list_ptr; Xint max_queue; X{ X X int i; X X /* X * Each line consists of the mark, and then the pathname. X */ X X fprintf(stderr, "Current Queue (Max = %d)\n-------------\n\n", max_queue); X for (i = 0; list_ptr; list_ptr = list_ptr->next, i++) { X X fprintf(stderr, "%d. %s\n", i, list_ptr->path); X } X X return TRUE; X} X Xboolean update_file( setd_fil, list_head, max_queue) Xchar *setd_fil; XLIST_NODE *list_head; Xint max_queue; X{ X X LIST_NODE *element, *last; X FILE *setd_fp; X X /* X * Write the new list of marks to the update file. X */ X X if (!(setd_fp = fopen(setd_fil, "w"))) { X X fprintf(stderr, "update_file: Unable to update %s\n", setd_fil); X return FALSE; X } X X fprintf(setd_fp, "%d\n", max_queue); X X /* X * Use a non-recursive inverted write to the file to speed things up, X * ever so slightly. There is a need for speed in this filter. X * The user shouldn't be aware of all the translations. X */ X X if (list_head) { X X last = NULL; X X /* X * Go to the last element, which hasn't been written yet. X */ X X do { X X for (element = list_head; element->next != last; element = element->next) ; X fprintf(setd_fp, "%s\n", element->path); X last = element; X } while (element != list_head); X } X X fclose(setd_fp); X return TRUE; X} X X/* X * boolean return_dest( char *dest, X * char *path, X * LIST_NODE *list, X * int list_length) X * X * return_dest attempts to translate the given path X * into a proper destination, whether an environment X * variable, mark, setd list position, or same level X * subdirectory. X */ X Xboolean return_dest( dest, X path, X list, X list_length) Xchar *path; Xchar *dest; XLIST_NODE *list; Xint list_length; X{ X X char temp[MAX_LINE]; X char front[MAX_LINE]; X char *getenv(); X char *mark; X char *env; X char *ptr; X int num = 0; X sprintf( temp, "mark_%s", path); X X /* X * Two types of mark uses are able to be recognized by X * setd. The first is just setting directory to the X * mark by itself. Then the translation is simple. X * The second method is by using the mark as a base, X * and adding an extended path after the mark. This X * method requires a reconstruction of a string to X * attempt proper mark resolution with the subdir string X * specified. X */ X X mark = getenv(temp); X env = getenv(path); X X strcpy( temp, path); X if (ptr = strchr( temp, '/')) { X X /* X * Zero out everything past the slash, and X * attempt to translate the temporary "mark" X * or environment variable. X */ X X *ptr = '\0'; X ptr = strchr( path, '/'); X sprintf( front, "mark_%s", temp); X if (mark = getenv( front)) { X X sprintf( temp, "%s%s", mark, ptr); X mark = temp; X } else if (env = getenv( front)) { X X sprintf( temp, "%s%s", env, ptr); X env = temp; X } X } X X if (chdir(path) == 0) { X X strcpy(dest, path); X } else if (mark) { X X strcpy(dest, mark); X } else if (env) { X X strcpy(dest, env); X } else if (conv_to_dec(path, &num)) { X X if ((num = abs(num)) > (list_length - 1)) { X X fprintf(stderr, "return_dest: out of bounds (-%d <= num <= %d)\n", X list_length, list_length); X return FALSE; X } X X for (; num; num--) list = list->next; X strcpy(dest, list->path); X X } else if (*path == '%') { X X sprintf( temp, "../%s", (path + 1)); X return (return_dest(dest, temp, list, list_length)); X X } else { X X /* X * Let normal cd handle error message. X */ X X strcpy(dest, path); X } X X return TRUE; X} X X/* X * boolean add_pwd( LIST_NODE *list_head, X * char *pwd, X * int max_queue, X * int *list_length, X * char *setd_fil) X * X * add_pwd attempts to push the given pwd onto the queue, unless X * the pwd is the same as the current pwd at the top of the stack. X */ X Xboolean add_pwd( list_head, X pwd, X max_queue, X list_length, X setd_fil) XLIST_NODE **list_head; Xint *list_length; Xint max_queue; Xchar *pwd; Xchar *setd_fil; X{ X X LIST_NODE *pwd_ptr; X X if (*list_head && STREQU((*list_head)->path, pwd)) return TRUE; X X pwd_ptr = (LIST_NODE *)malloc(sizeof(LIST_NODE)); X pwd_ptr->next = NULL; X pwd_ptr->path = (char *)malloc(strlen(pwd) + 1); X strcpy(pwd_ptr->path, pwd); X X push_list(list_head, pwd_ptr, list_length); X X if (*list_length > max_queue) free(shift_list(list_head, list_length)); X X if (!update_file( setd_fil, *list_head, max_queue)) { X X fprintf(stderr, "add_pwd: error in update_file\n"); X return FALSE; X } X X return TRUE; X} X X/* X * main( int argc, X * char *argv[]) X */ X Xmain(argc, argv) Xchar *argv[]; Xint argc; X{ X X LIST_NODE *list_head; X int old_max; X int max_queue; X int i; X char setd_fil[MAX_LINE]; X char *pwd; X char *home; X char dest[MAX_LINE]; X T_COMM option; X int list_length; X X list_head = NULL; X X /* X * First we must initialize, getting pointer to the X * mark file, and read in the current directory. X */ X X if (!initialize(&home, &pwd, setd_fil, &list_head, &max_queue, &list_length)) { X X fprintf(stderr, "setd: error code from initialize\n"); X exit(0); X } X X /* X * Push the current position onto the stack, if not already X * done. That is, if the current pwd is the same as the top X * of the stack, then do not add again. X */ X X if (!add_pwd( &list_head, pwd, max_queue, &list_length, setd_fil)) { X X fprintf( stderr, "setd: error from add_pwd\n"); X exit(-1); X } X X strcpy(dest, pwd); X X /* X * Now we have the file pointer for mark, and the current X * directory. It is time to start parsing the arguments X * on the command line. X */ X X for (i = 1; i <= argc; i++) { X X if (argc == 1) { X X option = K_SETD_HOME; X } else if (i == argc) { X X break; X } else if ((option = parse( argv[i])) == K_NULL) { X X X fprintf(stderr, "setd: error code from parse\n"); X } X X /* X * Switch, based upon the given option, to the X * proper function. X */ X X switch (option) { X X case K_VERSION: X fprintf(stderr, version_header); X break; X X case K_HELP: X fprintf(stderr, help_header); X break; X X case K_SETD_HOME: X strcpy(dest, home); X break; X X case K_SETD_DIR: X if (!return_dest(dest, argv[i], list_head, list_length)) { X X fprintf(stderr, "setd: error code from return_dest\n"); X } X break; X X case K_LIST_QUEUE: X if (!list_queue(list_head, max_queue)) { X X fprintf(stderr, "setd: error code from list_queue\n"); X } X break; X X case K_MAX_QUEUE: X X old_max = max_queue; X if ((++i >= argc) || (!conv_to_dec(argv[i], &max_queue)) X || (max_queue <= 0)) { X X fprintf(stderr, "setd: invalid maximum specified\n"); X } else { X X if (old_max != max_queue) list_head = NULL; X X if (!update_file( setd_fil, list_head, max_queue)) { X X fprintf(stderr, "setd: error in update_file\n"); X exit(-1); X } X } X break; X X default: break; X } X } X X fprintf(stdout, dest); X X exit(0); X} X END_OF_FILE if test 15273 -ne `wc -c <'setd.c'`; then echo shar: \"'setd.c'\" unpacked with wrong size! fi # end of 'setd.c' fi if test -f 'enum.h' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'enum.h'\" else echo shar: Extracting \"'enum.h'\" \(1289 characters\) sed "s/^X//" >'enum.h' <<'END_OF_FILE' X/* X * enum.h X * X * Enumerated Types File for both Mark and Set Directory X * X * Sunil William Savkar X * sunil@hal.com X * Copyright (c) 1991 X * All Rights Reserved X * X * DISCLOSURE X * X * This source may be modified or copied freely. The intent X * is the free distribution of a useful utility used for moving X * between directories. Any modifications and additions, along X * with bug reports should be sent to the author, so all might X * benefit! X * X * DESCRIPTION X * X * This module contains the enumerated type definitions for X * the command line arguments. Inclusive are a set of enumerations X * for such commands as the version, removal, inclusion, etcetera X * of marks and of set directory commands. The same file is included X * by both setd and mark. X */ X X/* X * typedef enum T_COMM X * X * The enumerated command arguments for mark. X */ X X#ifdef MARK Xtypedef enum T_COMM { X X K_VERSION, X K_HELP, X K_MARK_DIR, X K_REMOVE_MARK, X K_REFRESH_MARKS, X K_LIST_MARKS, X K_RESET_MARKS, X K_NULL X} T_COMM; X#endif X X/* X * typedef enum T_COMM X * X * The enumerated command arguments for setd. X */ X X#ifdef SETD Xtypedef enum T_COMM { X X K_SETD_DIR, X K_MAX_QUEUE, X K_SETD_HOME, X K_LIST_QUEUE, X K_POS_QUEUE, X K_BACK_QUEUE, X K_VERSION, X K_HELP, X K_NULL X} T_COMM; X#endif X END_OF_FILE if test 1289 -ne `wc -c <'enum.h'`; then echo shar: \"'enum.h'\" unpacked with wrong size! fi # end of 'enum.h' fi if test -f 'macros.h' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'macros.h'\" else echo shar: Extracting \"'macros.h'\" \(711 characters\) sed "s/^X//" >'macros.h' <<'END_OF_FILE' X/* X * macros.h X * X * Macros File for both Mark and Set Directory X * X * Sunil William Savkar X * sunil@hal.com X * Copyright (c) 1991 X * All Rights Reserved X * X * DISCLOSURE X * X * This source may be modified or copied freely. The intent X * is the free distribution of a useful utility used for moving X * between directories. Any modifications and additions, along X * with bug reports should be sent to the author, so all might X * benefit! X * X * DESCRIPTION X * X * This module contains all typed structures used by both X * setd and mark for changing directories or adding and/or X * deleting marks. X */ X X#define MAX_LINE 255 X#define FALSE 0 X#define TRUE 1 X#define STREQU(a, b) (strcmp( a, b) == 0) END_OF_FILE if test 711 -ne `wc -c <'macros.h'`; then echo shar: \"'macros.h'\" unpacked with wrong size! fi # end of 'macros.h' fi if test -f 'types.h' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'types.h'\" else echo shar: Extracting \"'types.h'\" \(1279 characters\) sed "s/^X//" >'types.h' <<'END_OF_FILE' X/* X * types.h X * X * Types File for both Mark and Set Directory X * X * Sunil William Savkar X * sunil@hal.com X * Copyright (c) 1991 X * All Rights Reserved X * X * DISCLOSURE X * X * This source may be modified or copied freely. The intent X * is the free distribution of a useful utility used for moving X * between directories. Any modifications and additions, along X * with bug reports should be sent to the author, so all might X * benefit! X * X * DESCRIPTION X * X * This module contains all typed structures used by both X * setd and mark for changing directories or adding and/or X * deleting marks. X */ X X/* X * typedef int boolean X * X * Type booleans to just be integers (TRUE or FALSE). X */ X Xtypedef int boolean; X X/* X * typedef struct COMMAND_NODE X * X * Structure used to store a string command and its enumerated X * equivalent. The enumerated equivalents can be found in enum.h X */ X Xtypedef struct COMMAND_NODE { X X T_COMM type; X char *text; X} COMMAND_NODE; X X/* X * typedef struct LIST_NODE X * X * This structure stores the list of marks, with the original X * mark, the path expansion for the mark, and a pointer to the X * next mark. X */ X Xtypedef struct LIST_NODE { X X int unset_flag; X char *mark; X char *path; X struct LIST_NODE *next; X} LIST_NODE; X X X X END_OF_FILE if test 1279 -ne `wc -c <'types.h'`; then echo shar: \"'types.h'\" unpacked with wrong size! fi # end of 'types.h' fi if test -f 'mark.1' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'mark.1'\" else echo shar: Extracting \"'mark.1'\" \(2549 characters\) sed "s/^X//" >'mark.1' <<'END_OF_FILE' X.\" @(#)mark.1 1.7 92/01/07 SMI; X.\" Updated 92/01/07 X.TH MARK 1 "07 January 1992" X.SH NAME X.TP 8 Xmark - XProgram to keep track of database of directory marks, with each mark representing an aliased directory. X.SH SYNOPSIS X.TP 7 X.B mark X[ X.B options X] X[ X.B directory mark X] X.SH DESCRIPTION X.LP X.B mark Xis a utility which is used in conjunction Xwith set directory, X.BR setd(1) X, to allow the user quick access to directory pathnames through marks. X.LP XThis program, combined with setd allows the user to freely Xmark directories using a string for quick access. X.SH INSTALLATION X.LP X.B mark Xinstallation is quick and painless. Both an environment Xvariable and a mark alias must be set to store the mark database Xand set-up mark to refresh the directory marks within the Xenvironment. Copying the three lines below for mark is all that Xis needed. X.LP X setenv MARK_DIR ~/bin X alias mark 'mark \!*; source $MARK_DIR/mark_db' X mark -refresh X.LP XIn the specific example, $MARK_DIR points to the user's bin Xarea, thus allowing the user to always have a database of Xmarks in a designated area for any number of processes. X.LP XThe alias of mark simply uses mark to set up the new line in Xthe mark database file, with the source command updating the Xenvironment variables used by setd. X.SH OPTIONS X.LP X.TP 12 X.B X.TP X.B -l XList directory marks. X.br XListing of all the marks set and their directory translation. X.TP X.B -rm X[ X.B mark X] X.TP X.B -remove X[ X.B mark X] X.br XRemove mark. X.br XRemoves the specified mark from the mark database. X.TP X.B -v XVersion number. X.br XOutputs the version of mark being run. X.TP X.B -h XShort help message. X.br XA condensed help message of the options. X.TP X.B -reset XReset marks. X.br XTruncates the mark database. X.TP X.B -r XRefresh marks. X.br XRefreshes the shell with the marks in the database. X.SH USAGE X.LP X.B mark Xcan be simply used by changing directory and then Xsetting a mark name by the following X.LP X mark [directory mark] X.LP Xwhich can then be used to change directory using X.B setd(1) Xfrom then on! To list marks, type mark and press return. XUpon logging in, a line is included in your .cshrc to Xinvisibly update the shell with the marks from the mark Xdatabase. If marks ever become corrupt, a simple refresh Xshould set things straight. X.SH FILES X$MARK_DIR/mark_db X.SH SEE ALSO X.B setd(1), cd(1) X.SH AUTHOR XSunil William Savkar X.br Xsunil@hal.com X.br XHaL Computer Systems Corporation X.br XDecember 26, 1991 X.SH VERSION XCurrently version 1.7, 1/7/92 X END_OF_FILE if test 2549 -ne `wc -c <'mark.1'`; then echo shar: \"'mark.1'\" unpacked with wrong size! fi # end of 'mark.1' fi if test -f 'setd.1' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'setd.1'\" else echo shar: Extracting \"'setd.1'\" \(4312 characters\) sed "s/^X//" >'setd.1' <<'END_OF_FILE' X.\" @(#)setd.1 1.7 92/01/07 SMI; X.\" Updated 92/01/07 X.TH SETD 1 "07 January 1992" X.SH NAME X.TP 8 Xsetd - XFilter program to change directory using marks, environment variables, or a built in queue. X.SH SYNOPSIS X.TP 7 X.B setd X[ X.B options X] X[ X.B directory X| X.B mark X[ X.B /directory X] | X.B env X| X.B offset X| X.B %directory X] X.TP X.B cd X[ X.B options X] X[ X.B directory X| X.B mark X[ X.B /directory X] | X.B env X| X.B offset X| X.B %directory X] X.SH DESCRIPTION X.LP X.B setd, set directory, is a filter utility interfaced Xwith change directory, X.BR cd(1) X, to allow the user quick access Xto directory pathnames through marks, environment variables, Xoffsets in a queue, etcetera. X.LP XCombined with the mark filter utility, X.BR mark(1) X, setd provides Xa very powerful method to access frequently used directories Xthrough mark aliases. setd can also translate through a mark Xwith a continuation of the directory description. Thus the Xuser can set a mark in a base directory and attach to sub- Xdirectories under the base simply by specifying the mark + '/' + Xsub-directory structure. X.LP Xsetd also includes a queue which tracks a history of directory Xpoints for the current process. The depth of this queue is Xconfigurable by the user, and a simple offset is used to access Xa location in the queue. X.LP XThe use of environment variables is also supported, along with Xtraversal along the same level of a directory tree. X.SH INSTALLATION X.LP X.B setd Xinstallation is quick and painless. Both an environment Xvariable and a cd alias must be set to store the queue database Xand set-up setd to filter into the change directory command Xrespectively. Copying the two lines below for setd is all that Xis needed. X.LP X setenv SETD_DIR /usr/tmp X alias cd 'cd `setd \!*`' X.LP XIn the specific example, $SETD_DIR points to the /usr/tmp area, Xthough most users will wish to actually set the pointer to their Xbin area instead (for example, ~/bin). X.LP XThe alias of cd simply filters all input through the setd program Xand directs the output to the cd command. To see setd in action, Xattempt to use setd separately, and notice the simple filtered Xoutput produced. When installed with the alias, all commands Xare seamless and accessible directly through cd. X.SH OPTIONS X.LP X.TP 10 X.B -l XList queue. X.br XHistory of past directory accesses, up to the maximum Xqueue depth specified by -max (or defaulting to 10). X.TP X.B -m XMax queue depth. X.br XSets maximum depth for the history queue (defaults to Xa maximum depth of 10 unless otherwise specified). X.TP X.B -v XVersion number. X.br XDisplays the version of setd being run. X.TP X.B -h XHelp message. X.br XEnumerates all the options. X.SH USAGE X.LP XWhen interfaced with cd, cd will perform exactly as before Xexcepting for special character sequences which are filtered Xby setd. Below are several examples of cd with the setd Xfilter. X.LP X.TP 4 X.B (1) cd [ directory ] XA straight directory string is given, thus cd automatically Xchanges to the given directory. X.TP X.B (2) cd [ mark ] XA mark alias was given, thus setd performs translation from Xthe mark to the corresponding directory. X.TP X.B (3) cd [ env ] XAn environment variable was specified, which is also translated Xfrom the variable into the corresponding directory. X.TP X.B (4) cd [ mark/directory ] Xsetd expands the given mark with the attached directory fully Xinto the translated mark with the directory appended to the Xtranslated path. X.TP X.B (5) cd [ offset ] XGiven a queue with a maximum depth of X, the offset number can Xtake on values from zero through the maximum value minus one. XThe offset values are symmetric around zero, thus the following Xtwo lines are equivalent: X.br X cd -1 X cd +1 X.br XBoth of these commands read the directory in position one off Xthe queue and set the user to the location. Notice position Xone corresponds to the last directory accessed. X.TP X.B (6) cd [ %directory ] XFinally, the percent (%) option can be placed in front of a Xdirectory name to allow the user to specify a directory at Xthe same level of hierarchy with the one currently set to. X.SH FILES X$SETD_DIR/setd_db X.SH SEE ALSO X.B mark(1), cd(1) X.SH AUTHOR XSunil William Savkar X.br Xsunil@hal.com X.br XHaL Computer Systems Corporation X.br XDecember 26, 1991 X.SH VERSION XCurrently version 1.7, 1/7/92 END_OF_FILE if test 4312 -ne `wc -c <'setd.1'`; then echo shar: \"'setd.1'\" unpacked with wrong size! fi # end of 'setd.1' fi if test -f 'Makefile' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'Makefile'\" else echo shar: Extracting \"'Makefile'\" \(1440 characters\) sed "s/^X//" >'Makefile' <<'END_OF_FILE' X# X# Makefile for setd and mark utilities X# Sunil William Savkar X# HaL Computer Sytems Corporation X# January 7, 1992 X# X# * Must change the BINDIR, MANDIR to point to the X# appropriate areas X# * Must change MACHINE_TYPE to reflect the type of machine X# you are running on (i.e. HP, SUN, RS6000, etcetera) X# X XBINDIR= /usr/bin XMANDIR = /usr/man/man1 X XTARGET1 = setd XTARGET2 = mark X XCC = cc XMACHINE_TYPE = SUN XOFLAGS = -O XCFLAGS = -D$(MACHINE_TYPE) $(OFLAGS) XLDFLAGS = XMAN1 = setd.1 XMAN2 = mark.1 XSOURCES1 = setd.c XSOURCES2 = mark.c XOBJECTS1 = setd.o XOBJECTS2 = mark.o XINCLUDES = X Xall: $(TARGET1) $(TARGET2) X X$(TARGET1): $(OBJECTS1) X $(CC) $(LDFLAGS) $(OBJECTS1) -o $(TARGET1) X$(TARGET2): $(OBJECTS2) X $(CC) $(LDFLAGS) $(OBJECTS2) -o $(TARGET2) Xsetd.o: types.h enum.h macros.h setd.c X $(CC) $(CFLAGS) -DSETD -c $(INCLUDES) setd.c Xmark.o: types.h enum.h macros.h mark.c X $(CC) $(CFLAGS) -DMARK -c $(INCLUDES) mark.c Xclean : X rm -f *.o X Xclobber : clean X rm -f $(TARGET1) X rm -f $(TARGET2) X Xinstall : all installman installexec X Xinstallman: X cp $(MAN1) $(MANDIR)/$(MAN1) X cp $(MAN2) $(MANDIR)/$(MAN2) X Xinstallexec: all X cp $(TARGET1) $(BINDIR)/$(MTYPE)/$(TARGET1) X cp $(TARGET2) $(BINDIR)/$(MTYPE)/$(TARGET2) Xtar: X rm -fr setd.src X mkdir setd.src X cp *.c setd.src X cp *.h setd.src X cp setd.1 setd.src X cp mark.1 setd.src X cp README setd.src X cp Makefile setd.src X tar -cf - setd.src | compress > setd.tar.Z X rm -fr setd.src END_OF_FILE if test 1440 -ne `wc -c <'Makefile'`; then echo shar: \"'Makefile'\" unpacked with wrong size! fi # end of 'Makefile' fi if test -f 'README' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'README'\" else echo shar: Extracting \"'README'\" \(1190 characters\) sed "s/^X//" >'README' <<'END_OF_FILE' XSetd and Mark Utilities XSunil William Savkar Xsunil@hal.com XHaL Computer Systems Inc. X XSetd and mark are two utilities useful for enhancing the change Xdirectory built in. Setd allow change directories using marks, Xenvironment variables, or a built in queue. X XMark is used to generate the translation list from directory Xmarks to full pathnames. X X1) Please read the man pages which come with the distribution X nroff -t -man setd.1 | more <== to view X nroff -t -man mark.1 | more X psroff -man setd.1 <== to print X psroff -man mark.1 X X2) Modify Makefile to have OFLAG reflect the type of machine you are X on. SUN, HP, IBM, etcetera. These two programs have been run on X Sparcs, HPs, IBM RS/6000s, Solbournes, and Sun 3s. It doesn't require X anything special installed. Therefore if you are using a machine other X than these, it will take little or no effort to get this to compile in X any flavour of UNIX. X X Also modify places to copy the files, put the man pages, etcetera. X X3) type make copy to copy to the /usr/bin area and to the man area. X X4) Remember to set the environment variables MARK_DIR and SETD_DIR X X5) Bugs or enhancements? Please update me at sunil@hal.com X X END_OF_FILE if test 1190 -ne `wc -c <'README'`; then echo shar: \"'README'\" unpacked with wrong size! fi # end of 'README' fi if test -f 'DISCLOSURE' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'DISCLOSURE'\" else echo shar: Extracting \"'DISCLOSURE'\" \(418 characters\) sed "s/^X//" >'DISCLOSURE' <<'END_OF_FILE' XSunil William Savkar Xsunil@hal.com XHaL Computer Systems Inc. X XThis source may be modified or copied freely. The intent Xis the free distribution of a useful utility used for moving Xbetween directories. Any modifications and additions, along Xwith bug reports should be sent to the author, so all might Xbenefit! X XHaL Computer Systems Corporation is in no way held legally or Xotherwise liable for the software herein. END_OF_FILE if test 418 -ne `wc -c <'DISCLOSURE'`; then echo shar: \"'DISCLOSURE'\" unpacked with wrong size! fi # end of 'DISCLOSURE' fi echo shar: End of shell archive. exit 0