/*--------------------------------------------------------------------*/ /* i m p o r t . c */ /* */ /* File name mapping routines for UUPC/extended */ /*--------------------------------------------------------------------*/ /*--------------------------------------------------------------------*/ /* Changes Copyright (c) 1989 by Andrew H. Derbyshire. */ /* */ /* Changes Copyright (c) 1990-1993 by Kendra Electronic */ /* Wonderworks. */ /* */ /* All rights reserved except those explicitly granted by the */ /* UUPC/extended license agreement. */ /*--------------------------------------------------------------------*/ /* * $Id: IMPORT.C 1.3 1993/04/11 00:31:31 dmwatt Exp $ * * $Log: IMPORT.C $ * Revision 1.3 1993/04/11 00:31:31 dmwatt * Global edits for year, TEXT, etc. * * Revision 1.2 1992/11/22 21:06:14 ahd * Correct mapping of dos paths with trailing slashes * */ #include #include #include #include #include #include "lib.h" #include "import.h" #include "arbmath.h" #include "hostable.h" #include "usertabl.h" #include "security.h" #define MAX_DIGITS 20 /* Number of digits for arb math */ /*--------------------------------------------------------------------*/ /* Internal function prototypes */ /*--------------------------------------------------------------------*/ #define min(x,y) (((x) < (y)) ? (x) : (y)) currentfile(); /*--------------------------------------------------------------------*/ /* Local function prototypes */ /*--------------------------------------------------------------------*/ static void ImportName( char *local, const char *canon, size_t charsetsize ); /*-------------------------------------------------------------------*/ /* */ /* i m p o r t p a t h */ /* */ /* Convert a canonical name to a format the host can handle */ /* */ /* These routines convert file name between canonical form, which */ /* is defined as a 'unix' style pathname, and the MS-DOS all */ /* uppercase "xxxxxxxx.xxx" format. */ /* */ /* If the canonical name does not have a path, that is the file is */ /* destined for the local spool directory, we can assume the UNIX */ /* name will normally be in a format like this: */ /* */ /* */ /* X.hostid####### (Execute files) */ /* C.hostid####### (Call files) */ /* D.hostid####### (Data files) */ /* */ /* where "hostid" may be most, but not always all, of the local */ /* host or remote host (the file came from or is going to) and */ /* "######" can be any character valid for the UNIX file system. */ /* Note, however, that the routine has to be generic to allow for */ /* other file names to be placed in the spool directory without */ /* collisions. */ /* */ /* Avoiding collisions in the spool directory is important; when */ /* receiving files with mixed case names longer than 11 */ /* characters, sooner or later a file name collision will occur. */ /* */ /* We can also assume that only UUPC will see these names, which */ /* means we can transform the name using any method we choose, so */ /* long as the UUPC functions opening the file always call */ /* importpath, and that importpath is reducible (that is, two */ /* calls to importpath with the same argument always yield the */ /* same result). Note that if end user really wanted the file in */ /* the spool directory, all he has to do is rename the file-- far */ /* better than losing the data because duplicate file names. */ /* */ /* For these files, we map the name as follows: */ /* */ /* 0 - If the name is a valid MS-DOS name, use it without changing */ /* */ /* 1 - Begin the output name by inserting up to the first eight */ /* characters of the remote host name (followed by a slash) as */ /* a subdirectory name. */ /* */ /* 2 - If the input name begins with an uppercase alphabetic */ /* character followed by a period, also insert the alphabetic */ /* (followed by a slash) to make this a second subdirectory. */ /* Then, move the logical start of the input name past the two */ /* characters. */ /* */ /* 3 - Determine the number of characters the local host and */ /* remote hosts have equal to the next characters of the input */ /* name, up to a maximum of 8, and zero the lower of the two */ /* counts. Then, step past the number of characters of the */ /* larger count. */ /* */ /* For example, if the file name is X.keane22222 and the local */ /* host name is kendra (2 characters match) and the remote */ /* host is keane1 (5 characters match), zero the number of */ /* characters matched by kendra, and make the new start of the */ /* file name five characters further (at the first "2"). */ /* */ /* 4 - Convert the remaining string using a base conversion, with */ /* the input character size being from ascii "#" to ascii "z" */ /* (88 characters) to the allowed set of characters in MS-DOS */ /* file names (charset, below, 52 characters). */ /* */ /* 5 - Prepend to the string to be converted the length of the */ /* remote host added to the length of the local host */ /* multiplied by 8 (both lengths were computed in step 3, */ /* above). The base conversion is also applied to this */ /* "character", we which know will be in the range 1-64. */ /* */ /* 6 - If the string created by steps 4 and 5 exceeds 8 */ /* characters, insert a period after the eighth character to */ /* make it a valid MS-DOS file name. If the string created by */ /* steps 4 and 5 exceeds 11 characters, truncate the string by */ /* using the first eight and last three characters. */ /* */ /* 7 - Append the string created in steps 4 through 6 to the path */ /* name created in steps 1 and 2. */ /* */ /* If the canonical name has a path, it is destined for an end */ /* user, so we should not radically transform it like we do for */ /* files in the spool directory. Thus, if the canonical name has */ /* a path, mung the canonical file name as follows: */ /* */ /* 1 - skip any path from the canonical name */ /* */ /* 2 - copy up to 8 character from the canonical name converting . */ /* to _ and uppercase to lowercase. */ /* */ /* 3 - if the name was longer than 8 character copy a . to the */ /* host name and then copy the up to three characters from */ /* the tail of the canonical name to the host name. */ /* */ /* Note that this set of rules will cause a collision with names */ /* that only differ in case, but leaves the name in a recongizable */ /* format for the user. */ /*-------------------------------------------------------------------*/ void importpath(char *local, char const *canon, char const *remote) { char *s, *out; size_t charsetsize; /* Number of allowed characters in MS-DOS file names */ out = local; /*--------------------------------------------------------------------*/ /* Verify our parameters */ /*--------------------------------------------------------------------*/ if ( local == NULL ) panic(); if ( canon == NULL ) panic(); /*--------------------------------------------------------------------*/ /* Define our character set */ /*--------------------------------------------------------------------*/ if ( E_charset == NULL ) E_charset = DOSCHARS; charsetsize = strlen( E_charset ); /*--------------------------------------------------------------------*/ /* Determine if spool file directory */ /*--------------------------------------------------------------------*/ if ((s = strrchr(canon, '/')) == (char *)NULL) { /* File for spooling directory, use internal character set to avoid collisons */ static size_t range = UNIX_END_C - UNIX_START_C + 1; /* Determine unique number characters in the UNIX file names we are mapping */ size_t remlen = min(HOSTLEN, strlen(remote)); /* Length of the remote name passed in, shortened below to number of characters matched in name */ size_t nodelen = min(HOSTLEN, strlen(E_nodename)); /* Length of the local host name, shortened below to number of characters matched in name */ size_t subscript = 0; /* Value of UNIX character to be converted to MS-DOS character set */ char *next = local + remlen; char tempname[FILENAME_MAX]; unsigned char number[MAX_DIGITS]; /* Arbitary length number, for base conversions */ /*--------------------------------------------------------------------*/ /* Verify we have a remote name */ /*--------------------------------------------------------------------*/ if ( remote == NULL ) panic(); /*--------------------------------------------------------------------*/ /* Put the host name (up to six characters) at the beginning of */ /* the MS-DOS file name as a sub-directory name. */ /*--------------------------------------------------------------------*/ strncpy(local, remote, remlen); *next++ = '/'; /* Add in the sub-directory seperator */ s = (char *) canon; /* Get the beginnging of the UNIX name */ /*--------------------------------------------------------------------*/ /* Files in the spooling directory generally start with "D.", */ /* "C.", or "X."; strip off any upper case letter followed by a */ /* period into its own directory. */ /*--------------------------------------------------------------------*/ if ((s[0] >= 'A') && (s[0] <= 'Z') && (s[1] == '.')) { *next++ = *s; /* Copy the input character */ *next++ = '/'; /* Add the sub-directory indicator too */ s += 2; /* Step input string past the copied data */ } while( remlen > 0 ) { if (equaln(remote,s,remlen)) break; remlen--; } while( nodelen > 0 ) { if (equaln(E_nodename,s,nodelen)) break; nodelen--; } if (nodelen > remlen ) { remlen = 0; s += nodelen; } else { nodelen = 0; s += remlen; } *next = '\0'; /* Terminate first part of host string */ /*--------------------------------------------------------------------*/ /* Create a binary number which represents our file name */ /*--------------------------------------------------------------------*/ for (subscript = 0; subscript < MAX_DIGITS; subscript++ ) number[subscript] = 0; /* Initialize number to zero */ add(number, nodelen + remlen * HOSTLEN, MAX_DIGITS); /* Append host name info to the front of the converted string */ while( (*s != '\0') && (*number == '\0')) { mult(number, range, MAX_DIGITS); /* Shift the number over */ add(number, *s++ - UNIX_START_C , MAX_DIGITS); /* Add in new low order */ } /* while */ /*-------------------------------------------------------------------*/ /* We now have stripped off the leading x. and host name, if any; */ /* now, convert the remaining characters in the name by doing a */ /* range to charset base conversion. */ /*-------------------------------------------------------------------*/ out = &tempname[FILENAME_MAX]; *--out = '\0'; /* Terminate the string we will build */ /*--------------------------------------------------------------------*/ /* Here's the loop to actually do the base conversion */ /*--------------------------------------------------------------------*/ while(adiv( number, charsetsize, &subscript, MAX_DIGITS)) *--out = E_charset[ subscript ]; /*--------------------------------------------------------------------*/ /* The conversion is done; now squeeze it into an 11 character */ /* MS-DOS name with period. */ /*--------------------------------------------------------------------*/ ImportName( next, out, charsetsize); } else { /* Not file for spooling directory, convert it */ char *in = (char *) canon; /*--------------------------------------------------------------------*/ /* Handle leading drive letter (ignore it, assuming valid) */ /*--------------------------------------------------------------------*/ if ( isalpha( *in ) && (in[1] == ':')) { *out++ = *in++; /* The drive letter */ *out++ = *in++; /* The colon making it a driver letter */ } /* if */ if ( *in == '/' ) /* Absolute path name? */ *out++ = *in++; /* Yes, step past it */ while( *in == '/') /* Additional slashes? */ in++; /* Skip them, they mean nothing */ s = strchr( in, '/' ); /* Get end of next path segment */ /*--------------------------------------------------------------------*/ /* Now convert each simple name in the path */ /*--------------------------------------------------------------------*/ while ( *in ) { if ( s != NULL ) *s = '\0'; /* Truncate input string to simple name */ ImportName( out, in , charsetsize ); if ( s == NULL ) break; out = out + strlen( out ); *out++ = *s++ = '/'; /* Restore path to input and output */ in = s; /* Remember start of this simple name */ while( *in == '/') /* Additional slashes? */ in++; /* Skip them, they mean nothing */ s = strchr( in , '/' ); } } /* else */ printmsg( 3, "ImportPath: Mapped %s to %s", canon, local ); } /*importpath*/ /*--------------------------------------------------------------------*/ /* I m p o r t N a m e */ /* */ /* Translate a simple DOS name without the path */ /*--------------------------------------------------------------------*/ static void ImportName( char *local, const char *canon, size_t charsetsize ) { char *in = (char *) canon; char *out = local; size_t len = strlen( canon ); size_t column; char *best_period = NULL; /* Assume no prince charming */ if ( strchr(canon,'/') != NULL ) { printmsg(0,"ImportName: Parameter error, not simple name: %s", canon); panic(); } if ( len == 0 ) { printmsg(0,"ImportName: Parameter error, zero length input"); panic(); } /*--------------------------------------------------------------------*/ /* If a valid DOS name, use it as-is */ /*--------------------------------------------------------------------*/ if (ValidDOSName( canon )) { strcpy( local, canon ); return; } /*--------------------------------------------------------------------*/ /* If the dataset name has a period, use it. The rule we */ /* follow is use the last period in the second through ninth */ /* characters, otherwise use the last period in the dataset */ /* name with the exception of leading period. */ /* */ /* In any case, we only copy up to eight characters for the */ /* dataset name and up to three characters for the extension. */ /*--------------------------------------------------------------------*/ for ( column = 1; (column < 9) && (in[column] != '\0') ; column++) { if ( in[column] == '.') { strncpy( out, in, column + 5 ); /* Period, 3 char extension, and terminating \0 */ best_period = &out[column];/* Remember output location of period in name */ if ( len > (column + 4) ) /* Need to trunc extension to 3? */ strcpy( out + column + 1, in + len - 3 ); /* Yes */ break; } /*if */ } /* if */ /*--------------------------------------------------------------------*/ /* No period in the first eight characters, search the rest of */ /* the name for the last period (unless period is very last */ /* character in the string). */ /*--------------------------------------------------------------------*/ if ( best_period == NULL ) { strncpy( out , in , 8); best_period = strrchr( in+1 , '.'); if ( (best_period != NULL) && (best_period[1] != '\0') ) { strncpy( &out[8], best_period, 4 ); /* Plus period and 3 in extension */ if ( strlen( best_period) > 4 ) /* Long Extension? */ out[12] = '\0'; /* Yes --> Truncate */ } /* if */ else { /* No periods at all, generate one if needed for long name */ if ( len > 8 ) { out[8] = '.'; strcpy(&out[9], in + max(8,(len - 3)) ); } /* if ( len > 9 ) */ } /* else */ best_period = &out[8]; /* Remember location of period, okay if past end of string */ } /* if ( best_period == NULL ) */ /*--------------------------------------------------------------------*/ /* Now, clean up any invalid characters */ /*--------------------------------------------------------------------*/ if ( out[ strlen( out ) - 1 ] == '.' ) /* Trailing period? */ out[ strlen( out ) - 1 ] = '\0'; /* Just truncate string */ while( *out != '\0') { int c ; if ( isupper( *out )) c = tolower( *out ); else c = *out; if ((out != best_period) && (strchr( E_charset, c ) == NULL )) { if ( c > 'z' ) c -= 62; else if ( c > 'Z' ) c -= 36; else if ( c > '9' ) c -= 10; *out = E_charset[ (c - UNIX_START_C) % charsetsize ]; } out++; /* Step to next character */ } /* while( *out != '\0') */ /*--------------------------------------------------------------------*/ /* Report our results and return */ /*--------------------------------------------------------------------*/ printmsg( 5, "ImportName: Mapped %s to %s", canon, local ); } /* ImportName */ /*--------------------------------------------------------------------*/ /* V a l i d D O S N a m e */ /* */ /* Validate an MS-DOS file name */ /*--------------------------------------------------------------------*/ boolean ValidDOSName( const char *s) { char *ptr; size_t len = strlen ( s ); char tempname[FILENAME_MAX]; /*--------------------------------------------------------------------*/ /* Define our character set */ /*--------------------------------------------------------------------*/ if ( E_charset == NULL ) E_charset = DOSCHARS; /*--------------------------------------------------------------------*/ /* Name must be 12 characters or less */ /*--------------------------------------------------------------------*/ if (len > 12) return FALSE; strcpy( tempname, s); /* Make a temp copy we can alter */ /*--------------------------------------------------------------------*/ /* Simple file name without extension must be eight chracters */ /* or less */ /*--------------------------------------------------------------------*/ ptr = strrchr(tempname, '.'); if (ptr == NULL) { if (len > 8) return FALSE; } /*--------------------------------------------------------------------*/ /* Period must be in second through ninth character */ /*--------------------------------------------------------------------*/ else { if ((ptr == tempname) || (ptr > &tempname[8])) return FALSE; /*--------------------------------------------------------------------*/ /* Extension must be three characters or less */ /*--------------------------------------------------------------------*/ if ( strlen( ptr ) > 4) /* Three characters plus the period? */ return FALSE; /* No --> Too much */ /*--------------------------------------------------------------------*/ /* Only one period */ /*--------------------------------------------------------------------*/ if (ptr != strchr(tempname, '.')) return FALSE; } /* else */ /*--------------------------------------------------------------------*/ /* Must only be valid MS-DOS characters */ /*--------------------------------------------------------------------*/ strlwr( tempname ); /* Map into our desired character set */ if ( ptr != NULL ) *ptr = 'x'; /* We've already accounted for the period, don't let it ruin our day */ if (strspn(tempname, E_charset ) == len) { printmsg(9,"ValidDOSName: \"%s\" is valid", s); return TRUE; } else return FALSE; } /* ValidateDOSName */