(This message is intended for the maintainer of the sh-utils. Although this 
is not a bug report, I'm sending it to this address as it is not immediately 
evident who the current maintainer of the package is)

I've written implementations of two small tools which I find useful, and 
which I believe would prove a nice addition to the sh-utils package.

The first is cleanname, based on a utility from Plan 9, which prints the 
shortest pathname equivalent to each path specified as an argument.
eg. `cleanname foo/.././bar//baz/` returns 'bar/baz'
This would fit in nicely alongside basename and dirname

The second is pick, an implementation of the tool described in Brian 
Kernighan and Rob Pike's book 'The Unix Programming Environment'
pick prompts the user for a response for each of its arguments, and echoes 
the argument if the user replies `y'
e.g. gzip `pick *.ps`
lets the user interactively select postscript files in the current directory 
to be compressed.

The attached source code should work `out of the box' when placed in the 
src/ subdirectory; if you decide to use the programs I could write some 
documentation for them as well.

-- 
+------------------------------+
| Leonard Stiles <[EMAIL PROTECTED]> |
+------------------------------+


/* cleanname -- print the shortest pathname equivalent to each specified NAME
   Copyright (C) 1990-1999 Free Software Foundation, Inc.

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2, or (at your option)
   any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software Foundation,
   Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */

#include <config.h>
#include <stdlib.h>
#include <stdio.h>
#include <getopt.h>

#include "error.h"
#include "system.h"
#include "xalloc.h"

#define AUTHORS         "Leonard Stiles"
#define PROGRAM_NAME    "cleanname"

#define DIRSEP  '/'

char *program_name;

static struct option const longopts[] = {
  {"directory", 1, NULL, 'd'},
  {GETOPT_HELP_OPTION_DECL},
  {GETOPT_VERSION_OPTION_DECL},
  {NULL, 0, NULL, 0}
};

void
usage (int status)
{
  if (status)
    fprintf (stderr, _("Try `%s --help' for more information.\n"),
             program_name);
  else
    {
      printf (_("Usage: %s [ -d PREFIX ] NAME...\n"), program_name);
      printf (_("\
Print the shortest pathname equivalent to each specified NAME.\n\
\n\
  -d, --directory=PREFIX   prefix relative pathnames with `PREFIX/'\n\
      --help               print this help and exit\n\
      --version            output version information and exit\n"));
      puts (_("\nReport bugs to <[EMAIL PROTECTED]>."));
    }
  exit (status);
}

/* return shortest pathname equivalent to s, rewriting it in place.
 * s must be at least two bytes long, to hold "." */
static char *
cleanname (char *s)
{
  int *stk = malloc (sizeof *s / 2); /* stack of dir. name indices */
  int iin = 0, iout = 0; /* input/output read/write indices */
  int firstchar, top = 0;

  /* return input unchanged if we can't allocate memory for stack */
  if (stk == NULL)
    return s;

  for (firstchar = 1; s[iin] != '\0'; firstchar = 0)
    {
      /* slash */
      if (s[iin] == DIRSEP)
        {
          if (firstchar || (iout > 0 && s[iout - 1] != DIRSEP))
            {
              s[iout++] = s[iin++];
            }
          else
            iin++;
          continue;
        }

      /* dots */
      if (s[iin] == '.' && (iout == 0 || s[iin - 1] == DIRSEP))
        {
          /* single dot */
          if (s[iin + 1] == DIRSEP || s[iin + 1] == '\0')
            {
              iin++;
              continue;
            }

          /* double dot */
          if (s[iin + 1] == '.'
              && (s[iin + 2] == DIRSEP || s[iin + 2] == '\0'))
            {
              if (top > 0)
                {
                  /* parent dir indices in stack */
                  iout = stk[--top];
                  iin += 2;
                }
              else              /* copy literally */
                while (s[iin] == '.')
                  s[iout++] = s[iin++];
              continue;
            }
        }

      /* non-special */
      if (iout == 0 || s[iout - 1] == DIRSEP)
        stk[top++] = iout;
      while (s[iin] && s[iin] != DIRSEP)
        s[iout++] = s[iin++];
    }

  if (iout == 0)
    s[iout++] = '.';
  s[iout] = '\0';
  /* delete trailing slashes */
  if (iout > 1 && s[iout - 1] == DIRSEP)
    s[iout - 1] = '\0';

  free (stk);
  return s;
}

int
main (int argc, char **argv)
{
  int i, optc;
  char *prefix = NULL;

  program_name = *argv;
  setlocale (LC_ALL, "");
  bindtextdomain (PACKAGE, LOCALEDIR);
  textdomain (PACKAGE);

  while ((optc = getopt_long (argc, argv, "d:", longopts, NULL)) != -1)
    switch (optc)
      {
      case 'd':
        prefix = optarg;
        break;
      case_GETOPT_HELP_CHAR;
      case_GETOPT_VERSION_CHAR (PROGRAM_NAME, AUTHORS);
      default:
        usage (1);
      }

  if (optind == argc)
    {
      error (0, 0, _("too few arguments"));
      usage (1);
    }

  for (i = optind; i < argc; i++)
    {
      char *instr;
      if (prefix != NULL && argv[i][0] != '/')
        {
          instr = xmalloc (strlen (prefix) + strlen (argv[i]) + 2);
          strcat (strcat (strcpy (instr, prefix), "/"), argv[i]);
        }
      else if (argv[i][0] == '\0')
        {
          instr = xmalloc (sizeof ".");
          *instr = '\0';
        }
      else
        instr = argv[i];

      puts (cleanname (instr));

      if (instr != argv[i]) free (instr);
    }
  return 0;
}
/* pick -- interactively select arguments to be echoed
   Copyright (C) 1990-1999 Free Software Foundation, Inc.

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU General Public License as published by
   the Free Software Foundation; either version 2, or (at your option)
   any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU General Public License
   along with this program; if not, write to the Free Software Foundation,
   Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.  */

#include <config.h>
#include <errno.h>
#include <stdio.h>

#include "error.h"
#include "long-options.h"
#include "readtokens.h"
#include "system.h"

#define PROGRAM_NAME "pick"
#define AUTHORS "Leonard Stiles"

#define DELIM "\n"

#define RESPFNAME "/dev/tty" /* whence to read user response */

char *program_name;

void
usage (int status)
{
  if (status != 0)
    fprintf (stderr, _("Try `%s --help' for more information.\n"),
             program_name);
  else
    {
      printf (_("\
Usage: %s ARGUMENT...\n\
  or:  %s OPTION\n\
"),
              program_name, program_name);
      printf (_("\
Interactively select arguments to be echoed\n\
\n\
  --help      display this help and exit\n\
  --version   output version information and exit\n\
\n\
For each ARGUMENT, prompt whether it should be echoed to the standard\n\
output; if the only argument is `-', read from the standard input.\n\
"));
      puts (_("\nReport bugs to <[EMAIL PROTECTED]>."));
    }
  exit (status);
}

void
pick (FILE* fp, char *s)
{
  int c;
  fprintf (stderr, "%s? ", s);
  while ((c = fgetc (fp)) == ' ')
    ;
  if (c == 'y') puts (s);
  else if (c == 'q' || c == EOF) exit (0);
  while (c != '\n')
    c = fgetc (fp);
}

int
main (int argc, char **argv)
{
  FILE *ttyf;

  program_name = *argv;
  
  setlocale (LC_ALL, "");
  bindtextdomain (PACKAGE, LOCALEDIR);
  textdomain (PACKAGE);

  parse_long_options (argc, argv, PROGRAM_NAME, GNU_PACKAGE, VERSION,
                      AUTHORS, usage);
  /* The above handles --help and --version.
     Since there is no other invocation of getopt, handle `--' here.  */
  if (argc > 1 && STREQ (argv[1], "--"))
    {
      --argc;
      ++argv;
    }

  if ((ttyf = fopen (RESPFNAME, "r")) == NULL)
      error (2, errno, _("error opening %s"), RESPFNAME);

  if (argc == 2 && STREQ (argv[1], "-"))
    {
      token_buffer tbuf;

      init_tokenbuffer (&tbuf);

      while (readtoken (stdin, DELIM, sizeof (DELIM) - 1, &tbuf) >= 0)
        pick (ttyf, tbuf.buffer);

      free (tbuf.buffer);
    }
  else
      while (--argc)
        pick (ttyf, *++argv);
  
  if (fclose (ttyf) != 0)
    error (2, errno, _("error closing %s"), RESPFNAME);

  return 0;
}

Reply via email to