home *** CD-ROM | disk | FTP | other *** search
/ Usenet 1994 January / usenetsourcesnewsgroupsinfomagicjanuary1994.iso / sources / unix / volume25 / setd / setd.c < prev    next >
Encoding:
C/C++ Source or Header  |  1992-01-11  |  14.9 KB  |  712 lines

  1. /*
  2.  *  SETD
  3.  *  Set Directory
  4.  *  Version 1.7
  5.  *
  6.  *  Sunil William Savkar
  7.  *  sunil@hal.com
  8.  *  Copyright (c) 1991
  9.  *  All Rights Reserved
  10.  *
  11.  *  DISCLOSURE
  12.  *
  13.  *  This source may be modified or copied freely.  The intent
  14.  *  is the free distribution of a useful utility used for moving
  15.  *  between directories.  Any modifications and additions, along
  16.  *  with bug reports should be sent to the author, so all might
  17.  *  benefit!
  18.  *
  19.  *  DESCRIPTION
  20.  * 
  21.  *  Set directory utility used in conjunction with the
  22.  *  mark command to allow ease of directory changes.
  23.  *
  24.  *  MODIFICATION HISTORY
  25.  *
  26.  *  8/2/91 Hunter Scales
  27.  *         Changed code to allow running on HPs.
  28.  *  8/20/91 Sunil William Savkar
  29.  *         Allow fall through of illegal directory
  30.  *         change so cd can try to use cdpath variable.
  31.  *  12/6/92 Sunil William Savkar
  32.  *         Added changes to allow a mark as a base of a
  33.  *         full pathname, thus not needing multiple
  34.  *         marks for subdirectories under a common point.
  35.  *
  36.  *  Please send all updates along with suggestions
  37.  *  to sunil@hal.com
  38.  */
  39.  
  40. #if (MACH == hp || MACH == m88k || MACH == rs)
  41. #include <stdlib.h>
  42. #else
  43. #include <alloca.h>
  44. #endif
  45. #include <stdio.h>
  46. #include <string.h>
  47. #include <ctype.h>
  48. #include "macros.h"
  49. #include "enum.h"
  50. #include "types.h"
  51.  
  52. /*
  53.  * Global Variable Definitions
  54.  */
  55.  
  56. COMMAND_NODE command[] = {
  57.  
  58.   K_SETD_DIR, "",
  59.   K_SETD_HOME, "",
  60.   K_POS_QUEUE, "",
  61.   K_BACK_QUEUE, "",
  62.   K_LIST_QUEUE, "-list", 
  63.   K_LIST_QUEUE, "-l",
  64.   K_MAX_QUEUE, "-max", 
  65.   K_MAX_QUEUE, "-m", 
  66.   K_HELP, "-h", 
  67.   K_HELP, "-help", 
  68.   K_VERSION, "-v", 
  69.   K_VERSION, "-ver", 
  70.   K_VERSION, "-version", 
  71.   K_NULL, NULL
  72. };
  73.  
  74. static char version_header[] = "Set Directory\tv1.7\tSunil William Savkar\n";
  75.  
  76. static  char    help_header[] =
  77. "Set Directory\tv1.7\nusage:\tcd <options>\n\n\
  78. option\t\tdescription\n\n\
  79. [path]\t\tAttempts change to specified directory pathname\n\
  80. [mark]\t\tAttempts change to directory specified by the mark alias\n\
  81. [mark]/[path]\tAttempts change to base mark plus appended pathname\n\
  82. [env]\t\tAttempts change to directory spec'd by environment variable\n\
  83. %%[path]\t\tAttempts change to subdirectory pathname of root one above\n\
  84. -l<ist>\t\tLists previous directories up to maximum set list length\n\
  85. -m<ax>\t\tSets the maximum depth of the past directory list\n\
  86. numeric\t\tChanges directory to specified list pos, or offset from top (-)\n\
  87. \nexamples:\tcd ~savkar, cd %bin, cd -4, cd MARK_NAME, cd MARK_NAME/xxx\n";
  88.  
  89. /*
  90.  *  boolean conv_to_dec( char *string,
  91.  *                       int  *conv_dec)
  92.  *
  93.  *  conv_to_dec translates the string to a decimal
  94.  *  value, and returns this decimal value in conv_dec.
  95.  *  If an error occurs during conversion, the function
  96.  *  returns false, else it returns true.
  97.  */
  98.  
  99. boolean conv_to_dec(string, 
  100.                     conv_dec)
  101. char *string;  /*  The string to convert                       */
  102. int *conv_dec; /*  The variable to store the converted integer */
  103. {
  104.  
  105.   int num = 0,  /*  Used to analyze and store shifted value    */
  106.       muln = 1; /*  +/- multiplier factor                      */
  107.  
  108.   /*
  109.    *  Check for the sign of the passed string
  110.    */
  111.  
  112.   if (*string == '-') {
  113.  
  114.     if (*(++string) == '\0') return FALSE;
  115.     muln = -1;
  116.   } else if (*string == '+') {
  117.  
  118.     if (*(++string) == '\0') return FALSE;
  119.   }
  120.  
  121.   /*
  122.    *  Increment through the string and put together the base ten
  123.    *  number which is represented.
  124.    */
  125.  
  126.   for (; *string != '\0'; string++) {
  127.  
  128.     if (!isdigit(*string)) return FALSE;
  129.     num = num*10 + (*string - '0');
  130.   }
  131.  
  132.   *conv_dec = num*muln;
  133.   return TRUE;
  134. }
  135.  
  136. /*
  137.  *  void push_list( LIST_NODE **list,
  138.  *                  LIST_NODE *element,
  139.  *                  int *length)
  140.  *
  141.  *  Push new node onto the list.
  142.  */
  143.  
  144. void push_list(list, element, length)
  145. LIST_NODE *element;
  146. LIST_NODE **list;
  147. int *length;
  148. {
  149.  
  150.   element->next = *list;
  151.   *list = element;
  152.   (*length)++;
  153. }
  154.  
  155. /*
  156.  *  LIST_NODE *shift_list( LIST_NODE *list,
  157.  *                         int *length)
  158.  *
  159.  *  Shift off the last element of the list, and pass
  160.  *  it to the caller.
  161.  */
  162.  
  163. LIST_NODE *shift_list(list, 
  164.                       length)
  165. LIST_NODE **list;
  166. int *length;
  167. {
  168.  
  169.   LIST_NODE *prev, *end;
  170.  
  171.   if (!(*list)) return NULL;
  172.  
  173.   if (!(*list)->next) {
  174.  
  175.     end = *list;
  176.     *list = NULL;
  177.   } else {
  178.  
  179.     for (end = *list; end->next; end = end->next) ;
  180.     for (prev = *list; prev->next != end; prev = prev->next) ;
  181.       prev->next = NULL;
  182.   }
  183.  
  184.   (*length)--;
  185.   return (end);
  186. }
  187.  
  188. /*
  189.  *  boolean read_into_list( char *setd_fil,
  190.  *                          LIST_NODE **list_head,
  191.  *                          int *max_queue,
  192.  *                          int *list_length)
  193.  *
  194.  *  read_into_list attempts to open the setd database and read
  195.  *  the list of prior directory changes into the queue.  An error
  196.  *  is returned if unable to open or read the setd db file. 
  197.  *
  198.  *  The database of prior locations is read into a list pointed at
  199.  *  by *list_head. The length of the list is in the list_length
  200.  *  variable, and the max_queue variable is also set at this point.
  201.  */
  202.  
  203. boolean read_into_list(setd_fil, list_head, max_queue, list_length)
  204. char *setd_fil;
  205. LIST_NODE **list_head;
  206. int *max_queue;
  207. int *list_length;
  208. {
  209.  
  210.   FILE *setd_fp;
  211.   LIST_NODE *new_ptr;
  212.   char path[MAX_LINE];
  213.  
  214.   *list_length = 0;
  215.  
  216.   if (!(setd_fp = fopen(setd_fil, "r"))) {
  217.  
  218.     fprintf(stderr, "read_into_list:  Unable to open %s\n", setd_fil);
  219.     return FALSE;
  220.   }
  221.  
  222.   /*
  223.    *  First argument is the maximum queue length.
  224.    */
  225.  
  226.   *max_queue = 0;
  227.   fscanf(setd_fp, "%d", max_queue);
  228.   if (!(*max_queue)) {
  229.  
  230.     *max_queue = 10;
  231.   }
  232.     
  233.   /*
  234.    *  Now continue reading the file and setting up the linked list.
  235.    */
  236.  
  237.   while (fscanf(setd_fp, "%s", path) == 1) {
  238.  
  239.     /*
  240.      *  Set up a new node.
  241.      */
  242.  
  243.     new_ptr = (LIST_NODE *)malloc(sizeof(LIST_NODE));
  244.     new_ptr->path = (char *)malloc(strlen(path) + 1);
  245.     new_ptr->next = NULL;
  246.     strcpy(new_ptr->path, path);
  247.  
  248.     push_list( list_head, new_ptr, list_length);
  249.   }
  250.  
  251.   fclose(setd_fp);
  252.   return TRUE;
  253. }
  254.  
  255. boolean initialize( home, pwd, setd_fil, list_head, max_queue, list_length)
  256. LIST_NODE **list_head;
  257. char *setd_fil;
  258. char **pwd;
  259. char **home;
  260. int *max_queue, *list_length;
  261. {
  262.  
  263.   FILE *setd_fp;
  264.   char *getenv();
  265.   char *ptr;
  266.  
  267.   /*
  268.    *  Get current directory and pointer to the mark directory.
  269.    */
  270.  
  271. #ifdef HP
  272.  
  273.   if (!(getcwd((*pwd = (char *)malloc(MAX_LINE), MAX_LINE))) {
  274. #else
  275.  
  276.   if (!(*pwd = getenv("PWD"))) {
  277. #endif  
  278.  
  279.     fprintf(stderr, "initialize:  Unable to get environment var $PWD\n");
  280.     return FALSE;
  281.   }
  282.  
  283. #ifdef HP
  284.  
  285.   /*
  286.    *  Notice we would like to strip off the /tmp_mount from the *pwd,
  287.    *  if it exists!
  288.    */
  289.  
  290.   if (strncmp(*pwd, "/tmp_mnt", 8) == 0) {
  291.     *pwd += 8;
  292.   }
  293. #endif
  294.  
  295.   if (!(*home = getenv("HOME"))) {
  296.  
  297.     fprintf(stderr, "initialize:  Unable to get environment var $HOME\n");
  298.     return FALSE;
  299.   }
  300.   
  301.   if (!(ptr = getenv("SETD_DIR"))) {
  302.  
  303.     fprintf(stderr, "initialize:  Must set environment var $SETD_DIR\n");
  304.     return FALSE;
  305.   } else {
  306.  
  307.     /*
  308.      *  Construct setd data base file pointer.
  309.      */
  310.  
  311.     sprintf( setd_fil, "%s/setd_db", ptr);
  312.   }
  313.  
  314.   if (!(setd_fp = fopen(setd_fil, "a"))) {
  315.  
  316.     fprintf(stderr, "initialize:  Unable to open %s\n", setd_fil);
  317.     return FALSE;
  318.   }
  319.  
  320.   fclose(setd_fp);
  321.  
  322.   /*
  323.    *  Read queue into a linked list, with the path string.
  324.    */
  325.  
  326.   if (!read_into_list(setd_fil, list_head, max_queue, list_length)) {
  327.  
  328.     fprintf(stderr, "initialize:  Unable to read setd file %s\n", setd_fil);
  329.     return FALSE;
  330.   }
  331.  
  332.   return TRUE;
  333. }  
  334.  
  335. T_COMM parse( option)
  336. char *option;
  337. {
  338.  
  339.   int j;
  340.   int num;
  341.   char *ptr;
  342.  
  343.   if (*option != '-') {
  344.  
  345.     return K_SETD_DIR;
  346.   }
  347.  
  348.   /*
  349.    *  Must be one of the commands, else there is
  350.    *  an error.
  351.    */
  352.  
  353.   for (j = 0; (ptr = command[j].text) && !STREQU( ptr, option); j++) ;
  354.  
  355.   if (!ptr) {
  356.  
  357.     if (conv_to_dec(option, &num)) return K_SETD_DIR;
  358.     fprintf(stderr, "parse: unrecognized option from main (%s)\n", option);
  359.     return K_NULL;
  360.   }
  361.  
  362.   return command[j].type;
  363. }
  364.  
  365. boolean list_queue( list_ptr, max_queue)
  366. LIST_NODE *list_ptr;
  367. int max_queue;
  368. {
  369.  
  370.   int i;
  371.   
  372.   /*
  373.    *  Each line consists of the mark, and then the pathname.
  374.    */
  375.  
  376.   fprintf(stderr, "Current Queue (Max = %d)\n-------------\n\n", max_queue);
  377.   for (i = 0; list_ptr; list_ptr = list_ptr->next, i++) {
  378.  
  379.     fprintf(stderr, "%d. %s\n", i, list_ptr->path);
  380.   }
  381.  
  382.   return TRUE;
  383. }
  384.  
  385. boolean update_file( setd_fil, list_head, max_queue)
  386. char *setd_fil;
  387. LIST_NODE *list_head;
  388. int max_queue;
  389. {
  390.  
  391.   LIST_NODE *element, *last;
  392.   FILE *setd_fp;
  393.  
  394.   /*
  395.    *  Write the new list of marks to the update file.
  396.    */
  397.  
  398.   if (!(setd_fp = fopen(setd_fil, "w"))) {
  399.  
  400.     fprintf(stderr, "update_file: Unable to update %s\n", setd_fil);
  401.     return FALSE;
  402.   }
  403.  
  404.   fprintf(setd_fp, "%d\n", max_queue);
  405.  
  406.   /*
  407.    *  Use a non-recursive inverted write to the file to speed things up,
  408.    *  ever so slightly.  There is a need for speed in this filter.
  409.    *  The user shouldn't be aware of all the translations.
  410.    */
  411.  
  412.   if (list_head) {
  413.  
  414.     last = NULL;
  415.  
  416.     /*
  417.      *  Go to the last element, which hasn't been written yet.
  418.      */
  419.  
  420.     do {
  421.  
  422.       for (element = list_head; element->next != last; element = element->next) ;
  423.       fprintf(setd_fp, "%s\n", element->path);
  424.       last = element;
  425.     } while (element != list_head);
  426.   }
  427.  
  428.   fclose(setd_fp);
  429.   return TRUE;
  430. }
  431.  
  432. /*
  433.  *  boolean return_dest( char *dest,
  434.  *                       char *path,
  435.  *                       LIST_NODE *list,
  436.  *                       int list_length)
  437.  *
  438.  *  return_dest attempts to translate the given path
  439.  *  into a proper destination, whether an environment
  440.  *  variable, mark, setd list position, or same level
  441.  *  subdirectory.
  442.  */
  443.  
  444. boolean return_dest( dest, 
  445.                      path, 
  446.                      list, 
  447.                      list_length)
  448. char *path;
  449. char *dest;
  450. LIST_NODE *list;
  451. int list_length;
  452. {
  453.  
  454.   char temp[MAX_LINE];
  455.   char front[MAX_LINE];
  456.   char *getenv();
  457.   char *mark;
  458.   char *env;
  459.   char *ptr;
  460.   int num = 0;
  461.   sprintf( temp, "mark_%s", path);
  462.  
  463.   /*
  464.    *  Two types of mark uses are able to be recognized by
  465.    *  setd.  The first is just setting directory to the
  466.    *  mark by itself.  Then the translation is simple.
  467.    *  The second method is by using the mark as a base,
  468.    *  and adding an extended path after the mark.  This
  469.    *  method requires a reconstruction of a string to
  470.    *  attempt proper mark resolution with the subdir string
  471.    *  specified.
  472.    */
  473.  
  474.   mark = getenv(temp);
  475.   env  = getenv(path);
  476.  
  477.   strcpy( temp, path);
  478.   if (ptr = strchr( temp, '/')) {
  479.  
  480.     /*
  481.      *  Zero out everything past the slash, and
  482.      *  attempt to translate the temporary "mark"
  483.      *  or environment variable.
  484.      */
  485.  
  486.     *ptr = '\0';
  487.     ptr = strchr( path, '/');
  488.     sprintf( front, "mark_%s", temp);
  489.     if (mark = getenv( front)) {
  490.  
  491.       sprintf( temp, "%s%s", mark, ptr);
  492.       mark = temp;
  493.     } else if (env = getenv( front)) {
  494.  
  495.       sprintf( temp, "%s%s", env, ptr);
  496.       env = temp;
  497.     }
  498.   }
  499.  
  500.   if (chdir(path) == 0) {
  501.  
  502.     strcpy(dest, path);
  503.   } else if (mark) {
  504.  
  505.     strcpy(dest, mark);
  506.   } else if (env) {
  507.  
  508.     strcpy(dest, env);
  509.   } else if (conv_to_dec(path, &num)) {
  510.  
  511.     if ((num = abs(num)) > (list_length - 1)) {
  512.  
  513.       fprintf(stderr, "return_dest: out of bounds (-%d <= num <= %d)\n", 
  514.               list_length, list_length);
  515.       return FALSE;
  516.     }
  517.  
  518.     for (; num; num--) list = list->next;
  519.     strcpy(dest, list->path);
  520.  
  521.   } else if (*path == '%') {
  522.  
  523.     sprintf( temp, "../%s", (path + 1));
  524.     return (return_dest(dest, temp, list, list_length));
  525.  
  526.   } else {
  527.  
  528.     /*
  529.      *  Let normal cd handle error message.
  530.      */
  531.  
  532.     strcpy(dest, path);    
  533.   }
  534.  
  535.   return TRUE;
  536. }
  537.  
  538. /*
  539.  * boolean add_pwd( LIST_NODE *list_head,
  540.  *                  char *pwd,
  541.  *                  int max_queue,
  542.  *                  int *list_length,
  543.  *                  char *setd_fil)
  544.  *
  545.  *  add_pwd attempts to push the given pwd onto the queue, unless
  546.  *  the pwd is the same as the current pwd at the top of the stack.
  547.  */
  548.  
  549. boolean add_pwd( list_head, 
  550.                  pwd, 
  551.                  max_queue, 
  552.                  list_length, 
  553.                  setd_fil)
  554. LIST_NODE **list_head;
  555. int *list_length;
  556. int max_queue;
  557. char *pwd;
  558. char *setd_fil;
  559. {
  560.  
  561.   LIST_NODE *pwd_ptr;
  562.  
  563.   if (*list_head && STREQU((*list_head)->path, pwd)) return TRUE;
  564.  
  565.   pwd_ptr = (LIST_NODE *)malloc(sizeof(LIST_NODE));
  566.   pwd_ptr->next = NULL;
  567.   pwd_ptr->path = (char *)malloc(strlen(pwd) + 1);
  568.   strcpy(pwd_ptr->path, pwd);
  569.   
  570.   push_list(list_head, pwd_ptr, list_length);
  571.  
  572.   if (*list_length > max_queue) free(shift_list(list_head, list_length));
  573.  
  574.   if (!update_file( setd_fil, *list_head, max_queue)) {
  575.  
  576.     fprintf(stderr, "add_pwd: error in update_file\n");
  577.     return FALSE;
  578.   }
  579.  
  580.   return TRUE;
  581. }
  582.  
  583. /*
  584.  *  main( int argc, 
  585.  *        char *argv[])
  586.  */
  587.  
  588. main(argc, argv)
  589. char *argv[];
  590. int argc;
  591. {
  592.  
  593.   LIST_NODE *list_head;
  594.   int old_max;
  595.   int max_queue;
  596.   int i;
  597.   char setd_fil[MAX_LINE];
  598.   char *pwd;
  599.   char *home;
  600.   char dest[MAX_LINE];
  601.   T_COMM option;
  602.   int list_length;  
  603.  
  604.   list_head = NULL;
  605.  
  606.   /*
  607.    *  First we must initialize, getting pointer to the
  608.    *  mark file, and read in the current directory.
  609.    */
  610.  
  611.   if (!initialize(&home, &pwd, setd_fil, &list_head, &max_queue, &list_length)) {
  612.  
  613.     fprintf(stderr, "setd: error code from initialize\n");
  614.     exit(0);
  615.   }
  616.  
  617.   /*
  618.    *  Push the current position onto the stack, if not already
  619.    *  done.  That is, if the current pwd is the same as the top
  620.    *  of the stack, then do not add again.
  621.    */
  622.  
  623.   if (!add_pwd( &list_head, pwd, max_queue, &list_length, setd_fil)) {
  624.  
  625.     fprintf( stderr, "setd: error from add_pwd\n");
  626.     exit(-1);
  627.   }
  628.  
  629.   strcpy(dest, pwd);
  630.  
  631.   /*
  632.    *  Now we have the file pointer for mark, and the current
  633.    *  directory.  It is time to start parsing the arguments
  634.    *  on the command line.
  635.    */
  636.  
  637.   for (i = 1; i <= argc; i++) {
  638.  
  639.     if (argc == 1) {
  640.  
  641.       option = K_SETD_HOME;
  642.     } else if (i == argc) {
  643.  
  644.        break;
  645.     } else if ((option = parse( argv[i])) == K_NULL) {
  646.  
  647.  
  648.       fprintf(stderr, "setd: error code from parse\n");
  649.     }
  650.  
  651.     /*
  652.      *  Switch, based upon the given option, to the
  653.      *  proper function.
  654.      */
  655.  
  656.     switch (option) {
  657.  
  658.     case K_VERSION:
  659.       fprintf(stderr, version_header);
  660.       break;
  661.  
  662.     case K_HELP:
  663.       fprintf(stderr, help_header);
  664.       break;
  665.  
  666.     case K_SETD_HOME:
  667.       strcpy(dest, home);
  668.       break;
  669.  
  670.     case K_SETD_DIR:
  671.       if (!return_dest(dest, argv[i], list_head, list_length)) {
  672.  
  673.         fprintf(stderr, "setd: error code from return_dest\n");
  674.       }
  675.       break;
  676.  
  677.     case K_LIST_QUEUE:
  678.       if (!list_queue(list_head, max_queue)) {
  679.  
  680.         fprintf(stderr, "setd: error code from list_queue\n");
  681.       }
  682.       break;
  683.  
  684.     case K_MAX_QUEUE:
  685.  
  686.       old_max = max_queue;
  687.       if ((++i >= argc) || (!conv_to_dec(argv[i], &max_queue))
  688.           || (max_queue <= 0)) {
  689.  
  690.         fprintf(stderr, "setd: invalid maximum specified\n");
  691.       } else {
  692.  
  693.         if (old_max != max_queue) list_head = NULL;
  694.  
  695.         if (!update_file( setd_fil, list_head, max_queue)) {
  696.  
  697.           fprintf(stderr, "setd: error in update_file\n");
  698.           exit(-1);
  699.         }
  700.       }
  701.       break;
  702.  
  703.     default: break;
  704.     }
  705.   }
  706.  
  707.   fprintf(stdout, dest);
  708.  
  709.   exit(0);
  710. }
  711.  
  712.