home *** CD-ROM | disk | FTP | other *** search
/ Fresh Fish 4 / FreshFish_May-June1994.bin / bbs / support / symlinks.c < prev    next >
C/C++ Source or Header  |  1994-05-14  |  10KB  |  444 lines

  1. /*
  2.  *  This is a quick 3 hour hack that is used to generate a batch file
  3.  *  that makes symbolic links (or else make the links directly), where
  4.  *  selected file from my CD-ROM can be linked into one of 26
  5.  *  subdirectories, according to the first letter of the name of the
  6.  *  file.
  7.  *
  8.  *  Given a file where each line is <pathname><space><version-number>,
  9.  *  such as:
  10.  *
  11.  *    cd0:BBS/ALib/d2xx/d234/MuchMore.lha 1.8
  12.  *
  13.  *  This program will emit two lines that look like:
  14.  * 
  15.  *    ln -s cd0:BBS/ALib/d2xx/d234/MuchMore.lha m/MuchMore-1.8.lha
  16.  *    ln -s cd0:BBS/ALib/d2xx/d234/MuchMore.lha.pi m/MuchMore-1.8.lha.pi
  17.  *
  18.  *  In the case where the version number is unknown (I.E. is ?.?),
  19.  *  the program will attempt to use the last component of the directory
  20.  *  path in place of the version number, if such component looks like
  21.  *  a "disk number".  I.E. "dNNN".  Thus the following:
  22.  *
  23.  *    cd0:BBS/ALib/d2xx/d234/MuchMore.lha ?.?
  24.  *
  25.  *  would produce:
  26.  *
  27.  *    ln -s cd0:BBS/ALib/d2xx/d234/MuchMore.lha m/MuchMore-d234.lha
  28.  *    ln -s cd0:BBS/ALib/d2xx/d234/MuchMore.lha.pi m/MuchMore-d234.lha.pi
  29.  *
  30.  *  If the last component of the directory name is not a "disk number",
  31.  *  then no version number is used.
  32.  *
  33.  *  The name of each link is remembered, and name collisions are handled
  34.  *  by appending "-a", "-b", "-c", etc to the version number, until a
  35.  *  suitable "-<letter>" is found that results in a unique name.
  36.  *  The mechanism used here (linear search of a linked list) is horribly
  37.  *  inefficient.  Then again, it processes all 6000+ file names from
  38.  *  my first FrozenFish CD-ROM with about 30 seconds of cpu time on
  39.  *  a 50Mhz 486, so it probably isn't worth trying to improve on until
  40.  *  processing time becomes too intolerable.
  41.  *
  42.  *  Options are:
  43.  *
  44.  *    -v   Print the name of each link to be make.
  45.  *
  46.  *    -s   Emit script to stdout rather than directly
  47.  *         making directories and links.
  48.  *
  49.  *    -V1  Assume "view 1", where each file is linked into
  50.  *         a subdirectory named for the first character
  51.  *         of the file.  Otherwise links are made in the
  52.  *           current directory.
  53.  *            
  54.  */
  55.  
  56. #include <stdio.h>
  57. #include <ctype.h>
  58.  
  59. #ifndef SEPARATE_GNU
  60. #define SEPARATE_GNU    0
  61. #endif
  62.  
  63. extern char *strchr ();
  64. extern char *strrchr ();
  65. extern char *strdup ();
  66. extern char *malloc ();
  67. extern char *strstr ();
  68. static char inbuf[256];
  69. static char linkto[64];
  70. static char nameroot[64];
  71. static char extension[64];
  72. static char linkname[64];
  73. static char dlinkname[64];
  74. static char copy[64];
  75. static char scratch1[256];
  76. static char scratch2[256];
  77.  
  78. struct namelink {
  79.   struct namelink *next;
  80.   char *name;
  81. };
  82.  
  83. struct namelink *names;
  84.  
  85. char *dnames[] =
  86. {
  87.   "a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m",
  88.   "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z",
  89. #if SEPARATE_GNU
  90.   "GNU",
  91. #endif
  92.   NULL
  93. };
  94.  
  95. char *numnames[] =
  96. {
  97.   "z", "o", "t", "t", "f", "f", "s", "s", "e", "n",
  98. };
  99.  
  100. int view = 0;
  101.  
  102. /* Look up a name in the linked list.  Return pointer to it if found,
  103.    otherwise NULL.  Do the comparison case independently, because
  104.    that is how the Amiga file system works. */
  105.  
  106. char *
  107. findname (char *name)
  108. {
  109.   struct namelink *scan;
  110.  
  111.   for (scan = names; scan != NULL; scan = scan ->next)
  112.     {
  113.       if (strcasecmp (name, scan -> name) == 0)
  114.     {
  115.       return (scan -> name);
  116.     }
  117.     }
  118.   return (NULL);
  119. }
  120.  
  121. /* Add a name to the linked list.  No testing for duplicates is done,
  122.    you should call findname() first to verify it is unique. */
  123.  
  124. void
  125. addname (char *name)
  126. {
  127.   struct namelink *new;
  128.  
  129.   new = (struct namelink *) malloc (sizeof (struct namelink));
  130.   new -> name = strdup (name);
  131.   new -> next = names;
  132.   names = new;
  133. }
  134.  
  135. char *
  136. dirname (char *sp)
  137. {
  138.   char *endp;
  139.   static char buf[256];
  140.  
  141.   strcpy (buf, sp);
  142.  
  143.   /* Look first for typical Unix or AmigaDOS separator */
  144.  
  145.   endp = strrchr (buf, '/');
  146.  
  147.   /* Look for special AmigaDOS separator */
  148.  
  149. #ifdef __amigados__
  150.   if (endp == NULL)
  151.     {
  152.       endp = strrchr (buf, ':');
  153.     }
  154. #endif
  155.  
  156.   /* If there was a separator, set up to zap it, otherwise the dirname
  157.      is just the empty string. */
  158.  
  159.   if (endp == NULL)
  160.     {
  161.       endp = buf;
  162.     }
  163.   *endp = '\000';
  164.   return (buf);
  165. }
  166.  
  167. char *
  168. basename (char *sp)
  169. {
  170.   char *bname;
  171.  
  172.   /* Look first for typical Unix or AmigaDOS separator */
  173.  
  174.   bname = strrchr (sp, '/');
  175.  
  176.   /* Look for special AmigaDOS separator */
  177.  
  178. #ifdef __amigados__
  179.   if (bname == NULL)
  180.     {
  181.       bname = strrchr (sp, ':');
  182.     }
  183. #endif
  184.  
  185.   /* If any separator found, skip over it, otherwise the
  186.      basename is just the entire string. */
  187.  
  188.   if (bname == NULL)
  189.     {
  190.       bname = sp;
  191.     }
  192.   else
  193.     {
  194.       bname++;
  195.     }
  196.  
  197.   return (bname);
  198. }
  199.  
  200. main (int argc, char **argv)
  201. {
  202.   char copychar;
  203.   char *verp;
  204.   char *scan;
  205.   char *dname = NULL;
  206.   char **pptr;
  207.   char ch;
  208.   int verbose = 0;
  209.   int script = 0;
  210.   extern int optind;
  211.   extern char *optarg;
  212.  
  213.   while ((ch = getopt (argc, argv, "vsV:")) != EOF)
  214.     {
  215.       switch (ch)
  216.     {
  217.     case 'V':
  218.       view = atoi (optarg);
  219.       break;
  220.     case 'v':
  221.       verbose++;
  222.       break;
  223.     case 's':
  224.       script++;
  225.       break;
  226.     }
  227.     }
  228.   if (view == 1)
  229.     {
  230.       for (pptr = dnames; *pptr != NULL; pptr++)
  231.     {
  232.       if (script)
  233.         {
  234.           printf ("mkdir %s\n", *pptr);
  235.         }
  236.       else
  237.         {
  238.           sprintf (scratch1, "%s", *pptr);
  239.           if (mkdir (scratch1) != 0)
  240.         {
  241.           perror (scratch1);
  242.         }
  243.         }
  244.     }
  245.     }
  246.   while (gets (inbuf) != NULL)
  247.     {
  248.       /* Separate pathname and version number */
  249.       verp = strchr (inbuf, ' ');
  250.       if (verp == NULL)
  251.     {
  252.       fprintf (stderr, "symlinks: missing separator: '%s'\n", inbuf);
  253.     }
  254.       *verp++ = '\000';
  255.       while (*verp == ' ')
  256.     {
  257.       verp++;
  258.     }
  259.  
  260.       /* Extract rootname and extension.  I.E. for "foo.tar.gz", 
  261.      get "foo" and ".tar.gz" */
  262.       strcpy (nameroot, basename (inbuf));
  263.       if (nameroot[0] == '\000')
  264.     {
  265.       /* This is a directory, not an archive. */
  266.       continue;
  267.     }
  268.       scan = strrchr (nameroot, '.');
  269.       if (scan == NULL)
  270.     {
  271.       /* No extension is fine.  It may just be a single file such
  272.          as an animation or picture. */
  273.       extension[0] = '\000';
  274.     }
  275.       else
  276.     {
  277.       if (strcmp (scan, ".gz") == 0)
  278.         {
  279.           strcpy (extension, scan);
  280.           *scan++ = '\000';
  281.           scan = strrchr (nameroot, '.');
  282.           if (strcmp (scan, ".tar") == 0)
  283.         {
  284.           *scan = '\000';
  285.           strcpy (extension, ".tar.gz");
  286.         }
  287.         }
  288.       else
  289.         {
  290.           strcpy (extension, scan);
  291.           *scan++ = '\000';
  292.         }
  293.     }
  294.  
  295.       /* Get version number */
  296.       if (*verp == '?')
  297.     {
  298.       /* Try using last component of pathname */
  299.       verp = basename (dirname (inbuf));
  300.       if (*verp != 'd' ||
  301.           !isdigit (*(verp + 1)) ||
  302.           !isdigit (*(verp + 2)) ||
  303.           !isdigit (*(verp + 3)))
  304.         {
  305.           verp = NULL;
  306.         }
  307.     }
  308.       if (verp != NULL)
  309.     {
  310.       /* If the name already contains the version string, suppress
  311.          adding another copy to it. */
  312.       if (strstr (nameroot, verp))
  313.         {
  314.           verp = NULL;
  315.         }
  316.     }
  317.       if (verp != NULL)
  318.     {
  319.       /* Check for the special case where the nameroot is DiskNNN.
  320.          There are almost 1000 of these for the floppy library, and
  321.          it doesn't make sense to have them printed as DiskNNN-dNNN
  322.          */
  323.       if ((strncmp (nameroot, "Disk", 4) == 0) &&
  324.           isdigit (nameroot[4]) &&
  325.           isdigit (nameroot[5]) &&
  326.           isdigit (nameroot[6]))
  327.         {
  328.           verp = NULL;
  329.         }
  330.     }
  331.       if (verp != NULL)
  332.     {
  333.       /* Replace any embedded blanks in the version string 
  334.          with '.' */
  335.       for (scan = verp; *scan != '\000'; scan++)
  336.         {
  337.           if (*scan == ' ')
  338.         {
  339.           *scan = '.';
  340.         }
  341.         }
  342.     }
  343.       copy[0] = '\000';
  344.       copychar = 'a';
  345.       for (;;)
  346.     {
  347.       if (verp != NULL)
  348.         {
  349.           sprintf (linkto, "%s-%s%s%s", nameroot, verp, copy,
  350.                extension);
  351.         }
  352.       else
  353.         {
  354.           sprintf (linkto, "%s%s%s", nameroot, copy, extension);
  355.         }
  356.  
  357.       /* Figure out which directory to put the linked name into.
  358.          Names that begin with numbers go in the directory that
  359.          is the first character of their full name.  I.E.
  360.          the file "3Dplot.lha" goes in the "t" directory, for
  361.          "three-Dplot.lha". */
  362.  
  363.       dlinkname[0] = '\000';
  364.       if (view == 1)
  365.         {
  366.           if (SEPARATE_GNU && (strstr (inbuf, "/GNU/") != NULL))
  367.         {
  368.