ftp.nice.ch/pub/next/unix/file/duplink.1.1.I.bs.tar.gz#/duplink1.1/Source/duplink.c

This is duplink.c in view mode; [Download] [Up]

/* DUPLINK, link duplicatie files in a directory tree.
   Copyright 1993 by Michael L.H. Brouwer (michael@urc.tue.nl)

   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., 675 Mass Ave, Cambridge, MA 02139, USA.

   $Modified: Mon Apr  5 00:56:47 1993 by michael $
   $Id: duplink.c,v 1.15 1993/04/04 22:57:24 michael Exp $  */

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif

#ifdef HAVE_LIBC_H
#include <libc.h>
#endif
#ifdef HAVE_UNISTD_H
#include <unistd.h>
#endif
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <stdarg.h>
#include <string.h>
#include <sys/types.h>
#include <sys/dir.h>
#include <sys/param.h>
#include <sys/stat.h>
#include <sys/wait.h>

#include "global.h"
#include "getopt.h"
#include "dirtree.h"

char *version_string = "duplink 1.1 (5 apr 93)";

/* No include file contains this one!  */
extern char *realpath (const char *file_name, char *resolved_name);

/* Command line options.  */
bool doit = FALSE;
bool shellout = TRUE;
bool remove_empty = TRUE;

static struct option longopts[] =
{
  {"doit", 0, NULL, 'd'},
  {"remove_empty", 0, NULL, 'e'},
  {"help", 0 ,NULL, 'h'},
  {"nowarn", 0, NULL, 'n'},
  {"quiet", 0, NULL, 'q'},
  {"silent", 0, NULL, 'q'},
  {"verbose", 0, NULL, 'v'},
  {"version", 0, NULL, 'V'},
  {NULL, 0, NULL, 0}
};

/* Link duplicate files in T.  Return 0 on succes and -1 on failure.  */
static int
link_same_files (dt_tree *t)
{
  dt_file *entry, *last_entry = NULL;
  char path1[MAXPATHLEN], path2[MAXPATHLEN];
  long last_id;
  int i;

  if (verbose)
    fprintf (stderr, "Linking identical files\n");
  sort_by_id (t);
  last_id = -1;
  for (i = t->elems, entry = t->files; i > 0; --i, entry++)
    {
      if (remove_empty && entry->size == 0)
	{
	  strcat (strcpy (path1, entry->dir), entry->name);
	  if (shellout)
	    printf ("rm %s\n", path1);
	  if (doit)
	    if (unlink (path1))
	      errorf ("unlink %s", path1);
	  last_id = entry->id;
	  last_entry = entry;
	}
      else if (last_id != entry->id)
	{
	  last_id = entry->id;
	  last_entry = entry;
	}
      else
	{
	  if (entry->device != last_entry->device)
	    {
	      if (warnings)
		errorf ("link %s%s %s%s imposible, filesystems differ.",
			last_entry->dir, last_entry->name,
			entry->dir, entry->name);
	    }
	  else if (entry->inode != last_entry->inode)
	    {
	      strcat (strcpy (path1, last_entry->dir), last_entry->name);
	      strcat (strcpy (path2, entry->dir), entry->name);
	      if (shellout)
		printf ("rm %s && ln %s %s\n", path2, path1, path2);
	      if (doit)
		{
		  if (unlink (path2))
		    errorf ("unlink %s", path2);
		  else if (link (path1, path2))
		    errorf ("link %s %s", path1, path2);
		}
	    }
	}
    }
  return 0;
} /* link_same_files */

/* Link duplicate files in DIR.  Return 0 on success and -1 on failure.  */
static int
duplink_dir (char *dir)
{
  char path[MAXPATHLEN];
  dt_tree *t;
  int files;

  /* Build name of store-file.  */
  if (!realpath (dir, path))
    {
      errorf ("realpath %s", path);
      return 1;
    }

  chdir (path);
  t = dt_read ();
  if (t == (dt_tree *) -1)
    return 1;
  files = dt_calc_ids (t);
  if (files == -1)
    return 1;
  if (link_same_files (t))
    return 1;
  return 0;
} /* duplink_dir */

/* If PRINTHELP is TRUE, print verbose help otherwise just show a short
   usage line.  Exit return code 4.  */
static void
usage (bool printhelp)
{
  if (!printhelp)
    fprintf (stderr, "\
usage: %s [-dehnqvV] dir...\n\
for more help, type: %s -h\n", prog, prog);
  else
    fprintf (stderr, "\
%s\n\
usage: %s [-dehnqvV] dir...\n\
 -d --doit		really link files (implies -n)\n\
 -e --remove_empty	remove files that are 0 bytes long\n\
 -h --help		give this help\n\
 -n --nowarn		don't print warnings for unrecognized files\n\
 -q --quiet		don't output a shell script\n\
    --silent\n\
 -v --verbose		verbose mode\n\
 -V --version		display version number\n\
 dir...			directories to scan.\n",
	     version_string, prog);
  exit (4);
} /* usage */

/* Yes, the main program, which parses arguments, and does the right thing
   with them.  */
int
main (int argc, char **argv)
{
  int opt;

  init_prog (argv[0]);
  while ((opt = getopt_long (argc, argv, "dehnqvV", longopts, (int *) 0))
	 != EOF)
    {
      switch (opt)
	{
	case 'd':
	  doit = TRUE;
	  shellout = FALSE;
	  break;
	case 'e':
	  remove_empty = TRUE;
	  break;
	case 'h':
	  usage (TRUE);
	case 'n':
	  warnings = FALSE;
	  break;
	case 'q':
	  shellout = FALSE;
	  break;
	case 'v':
	  verbose = TRUE;
	  break;
	case 'V':
	  fprintf (stderr, "%s\n", version_string);
	  break;
	default:
	  usage (FALSE);
	}
    }
  if (optind == argc)
    usage (FALSE);

  /* Loop though all the directory arguments.  Exit when the first one goes
     wrong.  */
  while (optind < argc)
    if (duplink_dir (argv[optind++]))
      return 1;
  return 0;
} /* main */

These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.