/*
** canonf v1.2.2
**
** Canonicalize filenames
**
** Copyright (C) 2.12.1997 by Andreas Ley <Andreas.Ley@rz.uni-karlsruhe.de>
**
** 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 of the License, 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.
**
** This program has been tested on a HP9000/715 with HP-UX A.09.07
** In this environment, neither lint -u nor gcc -Wall -pedantic -fsyntax-only
** produce any messages. If you encounter any errors or need to make any
** changes to port it to another platform, please contact me.
**
** Version history
**
** Version 1.2.2 - 14.5.2007
**	Added option -n
**
** Version 1.2.1 - 23.4.2007
**	Added option -M
**
** Version 1.2 - 1.10.2002
**	Added -q and -w options
**
** Version 1.1 - 7.9.2000
**	Fix directory prefix code, add -c option
**
** Version 1.0 - 2.12.1997
**	Initial version
*/

static char copyright[] = "@(#)Copyright (C) 1997,2007-2002 by Andreas Ley (Andreas.Ley@rz.uni-karlsruhe.de)";
static char sccsid[] = "@(#)canonf v1.2.2 - Canonicalize filenames";


#include <unistd.h>
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <dirent.h>
#include <errno.h>
#include <sys/stat.h>

#ifndef FALSE
#define	FALSE	(0)		/* This is the naked Truth */
#define	TRUE	(1)		/* and this is the Light */
#endif


char	*image;
int	debug=0,lower=FALSE,upper=FALSE,url=FALSE,mime=FALSE,mime_intro=FALSE,strip=FALSE,quotes=FALSE,whitespace=FALSE,force=FALSE,cmp=FALSE,dry=FALSE;

char	*uc="ABCDEFGHIJKLMNOPQRSTUVWXYZÀÁÂÃÄÅÆÇÈÉÊËÌÍÎÏÐÑÒÓÔÕÖØÙÚÛÜÝ";
char	*lc="abcdefghijklmnopqrstuvwxyzàáâãäåæçèéêëìíîïðñòóôõöøùúûüý";

static void canonf(file)
char	*file;
{
	char		nfile[1024],c,*ptr,*nptr,*mptr,*cptr,*sptr;
	struct stat	buf;
	int		skip=quotes;

	if (debug)
		(void)fprintf(stderr,"canonf(\"%s\")\n",file);

	if ((ptr=strrchr(file,'/')))
		ptr++;
	else
		ptr=file;
	if (debug>1)
		(void)fprintf(stderr,"canonf: ptr=\"%s\"\n",ptr);

	(void)strncpy(nfile,file,(size_t)(ptr-file));
	nptr=nfile+(ptr-file);
	if (debug>3) {
		(void)fprintf(stderr,"canonf: file=0x%08lx ptr=0x%08lx\n",file,ptr);
		(void)fprintf(stderr,"canonf: nfile=0x%08lx nptr=0x%08lx\n",nfile,nptr);
		*nptr='\0';
		(void)fprintf(stderr,"canonf: nfile=\"%s\"\n",nfile);
	}

	if (mime_intro) {
		if (!strncmp(ptr,"=?iso-8859-1?Q?",15))
			ptr+=15;
		else if (!strncmp(ptr,"=?iso-8859-15?Q?",16))
			ptr+=16;
		else if (!strncmp(ptr,"=?UTF-8?Q?",10))
			ptr+=10;
		if (debug>1)
			(void)fprintf(stderr,"mime_intro: ptr=\"%s\"\n",ptr);
	}

	while ((c=*ptr++)) {
		if (debug>2)
			(void)fprintf(stderr,"canonf: *ptr='%c'",c);
		if (((url&&c=='%')||(mime&&c=='=')) &&
			(('0'<=ptr[0]&&ptr[0]<='9')||('A'<=ptr[0]&&ptr[0]<='F')||('a'<=ptr[0]&&ptr[0]<='f')) &&
			(('0'<=ptr[1]&&ptr[1]<='9')||('A'<=ptr[1]&&ptr[1]<='F')||('a'<=ptr[1]&&ptr[1]<='f'))) {
			c=16*(ptr[0]<'A'?ptr[0]-'0':ptr[0]<'a'?ptr[0]-'A'+10:ptr[0]-'a'+10)+(ptr[1]<'A'?ptr[1]-'0':ptr[1]<'a'?ptr[1]-'A'+10:ptr[1]-'a'+10);
			ptr+=2;
		}
		if (whitespace&&((c&0x7f)<=' '||strchr("\"'\177",c))) {
			if (!skip)
				*nptr++='_';
		}
		else if (c=='/') {
			*nptr++='-';
			skip=FALSE;
			sptr=nptr;
		}
		else if (lower&&(cptr=strchr(uc,c))) {
			*nptr++=lc[cptr-uc];
			skip=FALSE;
			sptr=nptr;
		}
		else if (upper&&(cptr=strchr(lc,c))) {
			*nptr++=uc[cptr-lc];
			skip=FALSE;
			sptr=nptr;
		}
		else if (strip&&(c==';'))
			break;
		else if (!skip||!strchr("\"'",c)) {
			*nptr++=c;
			skip=FALSE;
			if (!strchr("\"'",c))
				sptr=nptr;
		}
		if (debug>2)
			(void)fprintf(stderr," *nptr='%c'\n",*(nptr-1));
		if (debug>3) {
			*nptr='\0';
			(void)fprintf(stderr,"\"%s\" -> \"%s\"\n",file,nfile);
		}
	} 
	if (quotes)
		nptr=sptr;
	*nptr='\0';
	if (mime_intro) {
		if (!strncmp(nptr-2,"?=",2))
			nptr[-2]='\0';
#ifndef NO_STRSTR
		else if (mptr=strstr(nfile,"?="))
			strcpy(mptr,mptr+2);
		if (mptr=strstr(nfile,"=?iso-8859-1?Q?"))
			strcpy(mptr,mptr+15);
		else if (mptr=strstr(nfile,"=?iso-8859-15?Q?"))
			strcpy(mptr,mptr+16);
#endif
	}

	if (!force&&!stat(nfile,&buf)) {
		errno=EEXIST;
		(void)fprintf(stderr,"%s: Can't rename %s to ",image,file);
		perror(nfile);
		if (cmp)
			(void)printf("cmp -s %s %s && rm %s\n",file,nfile,file);
	}
	else {
		if (debug>1)
			(void)fprintf(stderr,"rename(\"%s\",\"%s\")\n",file,nfile);
		if (dry) {
			(void)printf("mv \"%s\" \"%s\"\n",file,nfile);
		}
		else if (rename(file,nfile)) {
			(void)fprintf(stderr,"%s: Can't rename ",image);
			perror(file);
		}
	}
}



static void usage()
{
	(void)fprintf(stderr,"Usage: %s [options] [filename...]\n",image);
	(void)fprintf(stderr,"-l Change filenames to lower case\n");
	(void)fprintf(stderr,"-u Change filenames to upper case\n");
	(void)fprintf(stderr,"-d Decode URL encodings\n");
	(void)fprintf(stderr,"-m Decode MIME encodings\n");
	(void)fprintf(stderr,"-M Decode MIME encodings and strip MIME intro\n");
	(void)fprintf(stderr,"-s Strip version numbers\n");
	(void)fprintf(stderr,"-q Strip leading/trailing quotes and special characters\n");
	(void)fprintf(stderr,"-w Replace whitespace by underscores\n");
	(void)fprintf(stderr,"-f Force overwriting existing files\n");
	(void)fprintf(stderr,"-c Show cmp commands for duplicate files\n");
	(void)fprintf(stderr,"-n Dry run\n");
	exit(1);
}


int main(argc,argv)
int	argc;
char	*argv[];
{
	int		c;
	extern char	*optarg;
	extern int	optind;
	DIR		*dirp;
	struct dirent	*dp;

	if ((image=strrchr(argv[0],'/')))
		image++;
	else
		image=argv[0];

	while ((c=getopt(argc,argv,"DludmMsqwfcnvh?"))!=EOF)
		switch ((char)c) {
		case 'D':
			debug++;
			break;
		case 'l':
			lower=TRUE;
			break;
		case 'u':
			upper=TRUE;
			break;
		case 'd':
			url=TRUE;
			break;
		case 'm':
			mime=TRUE;
			break;
		case 'M':
			mime=TRUE;
			mime_intro=TRUE;
			break;
		case 's':
			strip=TRUE;
			break;
		case 'q':
			quotes=TRUE;
			break;
		case 'w':
			whitespace=TRUE;
			break;
		case 'f':
			force=TRUE;
			break;
		case 'c':
			cmp=TRUE;
			break;
		case 'n':
			dry=TRUE;
			break;
		case 'v':
			(void)fprintf(stderr,"%s\n",sccsid+4);
			(void)fprintf(stderr,"%s\n",copyright+4);
			exit(0);
		case 'h':
			(void)fprintf(stderr,"%s\n",sccsid+4);
			(void)fprintf(stderr,"%s\n",copyright+4);
		case '?':
			usage();
		}

	if (optind<argc)
		for (;optind<argc;optind++)
			canonf(argv[optind]);
	else {
		dirp=opendir(".");
		while ((dp=readdir(dirp)))
			if (strcmp(dp->d_name,".")&&strcmp(dp->d_name,"..")) {
				canonf(dp->d_name);
				errno=0;
			}
		if (errno) {
			(void)fprintf(stderr,"%s: Can't read directory ",image);
			perror(".");
			exit(1);
		}
	}

	return(0);
}

