char sccsID[] =
	"@(#) mvexp.c V1.0 Copyright Julian H. Stacey, Munich, 2022-11-22. \
	Public code free for others to use\n" ;
// code still being written
// This will supercede ~jhs/bin/.sh/nobrackets & noquotes

/* Strip nasty MS chars from a file name, that disturb CLI shells & scripts */
/* Named after Unix tr plus expand */

#include <stdio.h>
#include <string.h>
#include <sys/param.h> // MAXPATHLEN
#include <sys/types.h>
#include <sys/stat.h>
#include <ctype.h>

struct chars { char ch ; char *name ; } ;
struct chars expansions[] = {
/*
   expand() currently uses 0 as terminator of table,
   so JJLATER change expand(), so that we can look up a null byte

   The 3rd column used to be in upper case,
   but was converted to lower case, otherwise if a 2nd pass of this program
   was run on one tree but not the other, then the names would not match,
   & not be detectable by ~jhs/bin/.sh/dups
 */
//	   { 0x00 , /* 0x00 */ "_nul_"		} , // null
	   { 0x01 , /* 0x01 */ "_soh_"		} , // start of message
	   { 0x02 , /* 0x02 */ "_stx_"		} , // start of trasmission
	   { 0x03 , /* 0x03 */ "_etx_"		} , // end of trasmission
	   { 0x04 , /* 0x04 */ "_eot_"		} , // end of text
	   { 0x05 , /* 0x05 */ "_enq_"		} , // enquiry
	   { 0x06 , /* 0x06 */ "_ack_"		} , // acknowledgement
	   { 0x07 , /* 0x07 */ "_bel_"		} , // bell
	   { 0x08 , /* 0x08 */ "_bs_"		} , // back space
	   { 0x09 , /* 0x09 */ "_ht_"		} , // tab
	   { 0x0A , /* 0x0A */ "_nl_"		} , // new line
	   { 0x0B , /* 0x0B */ "_vt_"		} , // vertical tabulation
	   { 0x0C , /* 0x0C */ "_np_"		} , // new page
	   { 0x0D , /* 0x0D */ "_cr_"		} , // carriage return
	   { 0x0E , /* 0x0E */ "_so_"		} , // shift out
	   { 0x0F , /* 0x0F */ "_si_"		} , // shift in
	   { 0x10 , /* 0x10 */ "_dle_"		} , // delay?
	   { 0x11 , /* 0x11 */ "_dc1_"		} , // direct control 1
	   { 0x12 , /* 0x12 */ "_dc2_"		} , // direct control 2
	   { 0x13 , /* 0x13 */ "_dc3_"		} , // direct control 3
	   { 0x14 , /* 0x14 */ "_dc4_"		} , // direct control 4
	   { 0x15 , /* 0x15 */ "_nak_"		} , // negative acknowledgment
	   { 0x16 , /* 0x16 */ "_syn_"		} , // synchronisation
	   { 0x17 , /* 0x17 */ "_etb_"		} , // end text block
	   { 0x18 , /* 0x18 */ "_can_"		} , // cancel
	   { 0x19 , /* 0x19 */ "_em_"		} , // end message
	   { 0x1A , /* 0x1A */ "_sub_"		} , // ?sub?
	   { 0x1B , /* 0x1B */ "_esc_"		} , // escape
	   { 0x1C , /* 0x1C */ "_fs_"		} , // ?
	   { 0x1D , /* 0x1D */ "_gs_"		} , // ?
	   { 0x1E , /* 0x1E */ "_rs_"		} , // ?
	   { 0x1F , /* 0x1F */ "_us_"		} , // ?
	// { ' '  , /* 0x20 */ "_space_"	} ,
	   { ' '  , /* 0x20 */ "_"		} ,
	   { '!'  , /* 0x21 */ "_exclamation_"	} ,
	   { '"'  , /* 0x22 */ "_double_"	} ,
	   { '#'  , /* 0x23 */ "_hash_"		} ,
	   { '$'  , /* 0x24 */ "_dollar_"	} ,
	// { '%'  , /* 0x25 */ "_percent_"	} , // Not CLI disruptive.
	   { '&'  , /* 0x26 */ "_and_"		} , /* Ampersand */
	   { '\'' , /* 0x27 */ "_close-quote_"	} , /* Apostrophe */
	   { '('  , /* 0x28 */ "_open-bracket_"	} ,
	   { ')'  , /* 0x29 */ "_close-bracket_"} ,
	   { '*'  , /* 0x2a */ "_asterisk_"	} ,
	// { '+'  , /* 0x2b */ "_plus_"		} , // Not CLI disruptive.
	// { ','  , /* 0x2c */ "_comma_"	} , // Not CLI disruptive.
	// { '-'  , /* 0x2d */ "_minus_"	} , // Not CLI disruptive.
	// { '.'  , /* 0x2e */ "_dot_"		} , // Not CLI disruptive.
	// { '/'  , /* 0x2f */ "_slash_"	} , // Not CLI disruptive.
	// { ':'  , /* 0x3a */ "_colon_"	} , // Not CLI disruptive.
	   { ';'  , /* 0x3b */ "_semi_"		} , // Semi Colon
	   { '<'  , /* 0x3c */ "_less-than_"	} ,
	// { '='  , /* 0x3d */ "_equals_"	} , // Not CLI disruptive.
	   { '>'  , /* 0x3e */ "_more-than_"	} ,
	   { '?'  , /* 0x3f */ "_question_"	} ,
	   { '@'  , /* 0x40 */ "_at_"		} ,
	   { '['  , /* 0x5b */ "_open-square_"	} ,
	   { '\\' , /* 0x5c */ "_back-slash_"	} ,
	   { ']'  , /* 0x5d */ "_close-square_"	} ,
	// { '^'  , /* 0x5e */ "_up-circumflex_"} , // Not CLI disruptive.
	// { '_'  , /* 0x5f */ "_under-score_"	} , // Not CLI disruptive.
	   { '`'  , /* 0x60 */ "_open-quote_"	} ,
	   { '{'  , /* 0x7b */ "_open-brace_"	} ,
	   { '|'  , /* 0x7c */ "_pipe_"		} ,
	   { '}'  , /* 0x7d */ "_close-brace_"	} ,
	   { '~'  , /* 0x7e */ "_tilde_"	} ,
	   { 0x7f , /* 0x7f */ "_delete_"	} ,

/* { This is trimmed version of above, to edit & feed in to a grep script:

	_ack_		
	_and_		
	_asterisk_	
	_at_		
	_back-slash_	
	_bel_		
	_bs_		
	_can_		
	_close-bracket_
	_close-quote_	
	_close-square_	
	_colon_	
	_comma_	
	_cr_		
	_dc1_		
	_dc2_		
	_dc3_		
	_dc4_		
	_delete_	
	_dle_		
	_dollar_	
	_dot_		
	_double_	
	_em_		
	_enq_		
	_eot_		
	_equals_	
	_esc_		
	_etb_		
	_etx_		
	_exclamation_	
	_fs_		
	_gs_		
	_hash_		
	_ht_		
	_less-than_	
	_minus_	
	_more-than_	
	_nak_		
	_nl_		
	_np_		
	_open-brace_	
	_open-bracket_	
	_open-quote_	
	_open-square_	
	_percent_	
	_pipe_		
	_plus_		
	_question_	
	_rs_		
	_semi_		
	_si_		
	_slash_	
	_so_		
	_soh_		
	_space_	
	_stx_		
	_sub_		
	_syn_		
	_tilde_	
	_under-score_	
	_up-circumflex_
	_us_		
	_vt_		
	
	}*/

	/* The next block will get gradually filled in as I discover
	 * umlauts etc embedded in filenames downloaded from web.
	 */
	   // {
	   { 0x80 ,            "_0x80_"		} ,
	   { 0x81 ,            "_0x81_"		} ,
	   { 0x82 ,            "_0x82_"		} ,
	   { 0x83 ,            "_0x83_"		} ,
	   { 0x84 ,            "_0x84_"		} ,
	   { 0x85 ,            "_0x85_"		} ,
	   { 0x86 ,            "_0x86_"		} ,
	   { 0x87 ,            "_0x87_"		} ,
	   { 0x88 ,            "_0x88_"		} ,
	   { 0x89 ,            "_0x89_"		} ,
	   { 0x8a ,            "_0x8a_"		} ,
	   { 0x8b ,            "_0x8b_"		} ,
	   { 0x8c ,            "_0x8c_"		} ,
	   { 0x8d ,            "_0x8d_"		} ,
	   { 0x8e ,            "_0x8e_"		} ,
	   { 0x8f ,            "_0x8f_"		} ,
	   { 0x90 ,            "_0x90_"		} ,
	   { 0x91 ,            "_0x91_"		} ,
	   { 0x92 ,            "_0x92_"		} ,
	   { 0x93 ,            "_0x93_"		} ,
	   { 0x94 ,            "_0x94_"		} ,
	   { 0x95 ,            "_0x95_"		} ,
	   { 0x96 ,            "_0x96_"		} ,
	   { 0x97 ,            "_0x97_"		} ,
	   { 0x98 ,            "_0x98_"		} ,
	   { 0x99 ,            "_0x99_"		} ,
	   { 0x9a ,            "_0x9a_"		} ,
	   { 0x9b ,            "_0x9b_"		} ,
	   { 0x9c ,            "_0x9c_"		} ,
	   { 0x9d ,            "_0x9d_"		} ,
	   { 0x9e ,            "_0x9e_"		} ,
	   { 0x9f ,            "_0x9f_"		} ,
	   { 0xa0 ,            "_0xa0_"		} ,
	   { 0xa1 ,            "_0xa1_"		} ,
	   { 0xa2 ,            "_0xa2_"		} ,
	   { 0xa3 ,            "_0xa3_"		} ,
	   { 0xa4 ,            "_0xa4_"		} ,
	   { 0xa5 ,            "_0xa5_"		} ,
	   { 0xa6 ,            "_0xa6_"		} ,
	   { 0xa7 ,            "_0xa7_"		} ,
	   { 0xa8 ,            "_0xa8_"		} ,
	   { 0xa9 ,            "_0xa9_"		} ,
	   { 0xaa ,            "_0xaa_"		} ,
	   { 0xab ,            "_0xab_"		} ,
	   { 0xac ,            "_0xac_"		} ,
	   { 0xad ,            "_0xad_"		} ,
	   { 0xae ,            "_0xae_"		} ,
	   { 0xaf ,            "_0xaf_"		} ,
	   { 0xb0 ,            "_0xb0_"		} ,
	   { 0xb1 ,            "_0xb1_"		} ,
	   { 0xb2 ,            "_0xb2_"		} ,
	   { 0xb3 ,            "_0xb3_"		} ,
	   { 0xb4 ,            "_0xb4_"		} ,
	   { 0xb5 ,            "_0xb5_"		} ,
	   { 0xb6 ,            "_0xb6_"		} ,
	   { 0xb7 ,            "_0xb7_"		} ,
	   { 0xb8 ,            "_0xb8_"		} ,
	   { 0xb9 ,            "_0xb9_"		} ,
	   { 0xba ,            "_0xba_"		} ,
	   { 0xbb ,            "_0xbb_"		} ,
	   { 0xbc ,            "_0xbc_"		} ,
	   { 0xbd ,            "_0xbd_"		} ,
	   { 0xbe ,            "_0xbe_"		} ,
	   { 0xbf ,            "_0xbf_"		} ,
	   { 0xc0 ,            "_0xc0_"		} ,
	   { 0xc1 ,            "_0xc1_"		} ,
	   { 0xc2 ,            "_0xc2_"		} ,
	   { 0xc3 ,            "_0xc3_"		} ,
	   { 0xc4 ,            "_0xc4_"		} ,
	   { 0xc5 ,            "_0xc5_"		} ,
	   { 0xc6 ,            "_0xc6_"		} ,
	   { 0xc7 ,            "_0xc7_"		} ,
	   { 0xc8 ,            "_0xc8_"		} ,
	   { 0xc9 ,            "_0xc9_"		} ,
	   { 0xca ,            "_0xca_"		} ,
	   { 0xcb ,            "_0xcb_"		} ,
	   { 0xcc ,            "_0xcc_"		} ,
	   { 0xcd ,            "_0xcd_"		} ,
	   { 0xce ,            "_0xce_"		} ,
	   { 0xcf ,            "_0xcf_"		} ,
	   { 0xd0 ,            "_0xd0_"		} ,
	   { 0xd1 ,            "_0xd1_"		} ,
	   { 0xd2 ,            "_0xd2_"		} ,
	   { 0xd3 ,            "_0xd3_"		} ,
	   { 0xd4 ,            "_0xd4_"		} ,
	   { 0xd5 ,            "_0xd5_"		} ,
	   { 0xd6 ,            "_0xd6_"		} ,
	   { 0xd7 ,            "_0xd7_"		} ,
	   { 0xd8 ,            "_0xd8_"		} ,
	   { 0xd9 ,            "_0xd9_"		} ,
	   { 0xda ,            "_0xda_"		} ,
	   { 0xdb ,            "_0xdb_"		} ,
	   { 0xdc ,            "_0xdc_"		} ,
	   { 0xdd ,            "_0xdd_"		} ,
	   { 0xde ,            "_0xde_"		} ,
	   { 0xdf ,            "_0xdf_"		} ,
	   { 0xe0 ,            "_0xe0_"		} ,
	   { 0xe1 ,            "_0xe1_"		} ,
	   { 0xe2 ,            "_0xe2_"		} ,
	   { 0xe3 ,            "_0xe3_"		} ,
	   { 0xe4 ,            "_0xe4_"		} ,
	   { 0xe5 ,            "_0xe5_"		} ,
	   { 0xe6 ,            "_0xe6_"		} ,
	   { 0xe7 ,            "_0xe7_"		} ,
	   { 0xe8 ,            "_0xe8_"		} ,
	   { 0xe9 ,            "_0xe9_"		} ,
	   { 0xea ,            "_0xea_"		} ,
	   { 0xeb ,            "_0xeb_"		} ,
	   { 0xec ,            "_0xec_"		} ,
	   { 0xed ,            "_0xed_"		} ,
	   { 0xee ,            "_0xee_"		} ,
	   { 0xef ,            "_0xef_"		} ,
	   { 0xf0 ,            "_0xf0_"		} ,
	   { 0xf1 ,            "_0xf1_"		} ,
	   { 0xf2 ,            "_0xf2_"		} ,
	   { 0xf3 ,            "_0xf3_"		} ,
	   { 0xf4 ,            "_0xf4_"		} ,
	   { 0xf5 ,            "_0xf5_"		} ,
	   { 0xf6 ,            "_0xf6_"		} ,
	   { 0xf7 ,            "_0xf7_"		} ,
	   { 0xf8 ,            "_0xf8_"		} ,
	   { 0xf9 ,            "_0xf9_"		} ,
	   { 0xfa ,            "_0xfa_"		} ,
	   { 0xfb ,            "_0xfb_"		} ,
	   { 0xfc ,            "_0xfc_"		} ,
	   { 0xfd ,            "_0xfd_"		} ,
	   { 0xfe ,            "_0xfe_"		} ,
	   { 0xff ,            "_0xff_"		} ,
	   // }
	   { 0    , /* 0    */ "\0"		} } ;

char *expand(char in)	// Return a null string pointer if no matching char
	{
	struct chars *p ;
	extern struct chars expansions[] ;

	for ( p = &expansions[0] ;
			( p->ch != 0 ) && ( p->ch != in ) ;
			 )
		{
		p++ ;
		}
	return p->name ;
	}

int main(int argc, char **argv)
	{
	int ARGC; char **ARGV ;
	char *oldname ;
	char newname[MAXPATHLEN] ;
	char *old_p, *new_p ;
	char *match ;
	// extern char *expand();
	unsigned verbose = 0 ;		// JJLATER will be set by argv -v
	unsigned forcelower = 1 ;	// JJLATER option will be set by argv -v
	int result ;
	int failures = 0 ;		// Cumulative count of failed moves..
	struct stat avoid_overwrite ;

	ARGC = argc ; ARGV = argv ;

	argc-- ; argv++ ;
	while (argc--)	// xxx-continue
		{
		oldname = *argv++ ;
		// Generate new name
		// JJLATER add prevention for overflowing MAXPATHLEN
		for ( old_p=oldname, new_p=newname, newname[0] = '\0' ;
			*old_p != '\0' ; )
			{
			// printf("Debug2: %c\n",*old_p);
			match = expand(*old_p) ;
			// printf("Debug1: %d %s\n", argc, *argv);
			if ( *match == '\0' )
				{
				if (forcelower)
					*new_p++ = tolower(*old_p ) ;
				else
					*new_p++ = *old_p ;
				*new_p = '\0' ;
				}
			else	{
				// printf("Debug3: %s\n",match);
				strcat(new_p, match);
				while ( *new_p != '\0' ) new_p++ ;
				}
			old_p++ ;
			}
		// printf("Old Name: %s\n", oldname);
		// printf("New Name: %s\n", newname);

		if (strcmp(oldname,newname) == 0) continue ; // xxx-continue
			// Humans wont tend to manually feed in
			// names that dont need converting, but
			// /usr/bin/find may feed in screeds of names
			// that mostly do not need moving

		if (verbose)
			printf("Checking existance of old %s\n", oldname);
		if ( result = stat(oldname,&avoid_overwrite) )
			{	/* -1 */
			printf("Non existant %s\n",
				(verbose) ? "" : oldname);
			// perror("\0") ;
			failures += 1 ;
			}
		else	{ /* 0: Old name exist, stay silent */
			}

		if  (!result)
			{
			if (verbose)
				printf("Checking existance of new %s\n", newname);
			if ( result = stat(newname,&avoid_overwrite) )
				{
				/* -1 If new name fail to exist.  */
				result = 0 ; // Convert negative test.
				}
			else {
				/* 0 If new name does pre-exist, it should not exist.  */
				printf("Avoiding overwriting pre-existant old %s to new %s\n",
					(verbose) ? "" : oldname ,
					(verbose) ? "" : newname
					);
				// perror("\0") ;
				failures += 1 ;
				result = 1 ; // Convert negative test.
				}
			}
		if (!result)
			{
			if (verbose)
                printf("Moving %s to %s\n", oldname, newname);
			if ( result = rename(oldname,newname) )
				{
				printf("Failed to move %s to %s", oldname, newname);
				perror("\0") ;
				failures += 1 ;
				}
			}
		// printf("\n");
		}
	if (failures) fprintf(stderr,"%s: %d failures\n", *ARGV, failures );
	return(failures);
	}
