This repository has been archived on 2024-04-08. You can view files and clone it, but cannot push or open issues or pull requests.
deb-mbse/lib/mangle.c
2004-01-28 21:10:56 +00:00

475 lines
12 KiB
C

/*****************************************************************************
*
* $Id$
* Purpose ...............: Mangle a unix name to DOS 8.3 filename
*
*****************************************************************************
* Copyright (C) 1997-2004
*
* Michiel Broek FIDO: 2:280/2802
* Beekmansbos 10
* 1971 BV IJmuiden
* the Netherlands
*
* This file is part of MBSE BBS.
*
* This BBS 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.
*
* MBSE BBS 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 MBSE BBS; see the file COPYING. If not, write to the Free
* Software Foundation, 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA.
*****************************************************************************
* Ideas taken from Samba, Copyright (C) Andrew Tridgell 1992-1998
*****************************************************************************/
#include "../config.h"
#include "libs.h"
#include "structs.h"
#include "clcomm.h"
#include "common.h"
/*
* Prototype functions
*/
int strhaslower(const char *);
char *safe_strcpy(char *, const char *, size_t);
static void init_chartest(void);
static int is_reserved_msdos(char *);
int is_8_3(char *);
#define PTR_DIFF(p1,p2) ((int)(((const char *)(p1)) - (const char *)(p2)))
/* -------------------------------------------------------------------------- **
* Other stuff...
*
* magic_char - This is the magic char used for mangling. It's global.
*
* MANGLE_BASE - This is the number of characters we use for name mangling.
*
* basechars - The set characters used for name mangling. This
* is static (scope is this file only).
*
* mangle() - Macro used to select a character from basechars (i.e.,
* mangle(n) will return the nth digit, modulo MANGLE_BASE).
*
* chartest - array 0..255. The index range is the set of all possible
* values of a byte. For each byte value, the content is a
* two nibble pair. See BASECHAR_MASK and ILLEGAL_MASK,
* below.
*
* ct_initialized - False until the chartest array has been initialized via
* a call to init_chartest().
*
* BASECHAR_MASK - Masks the upper nibble of a one-byte value.
*
* ILLEGAL_MASK - Masks the lower nibble of a one-byte value.
*
* isillegal() - Given a character, check the chartest array to see
* if that character is in the illegal characters set.
* This is faster than using strchr().
*
*/
char magic_char = '~';
static char basechars[] = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ_-!@#$%";
#define MANGLE_BASE (sizeof(basechars)/sizeof(char)-1)
static unsigned char chartest[256] = { 0 };
static int ct_initialized = FALSE;
#define mangle(V) ((char)(basechars[(V) % MANGLE_BASE]))
#define BASECHAR_MASK 0xf0
#define ILLEGAL_MASK 0x0f
#define isillegal(C) ( (chartest[ ((C) & 0xff) ]) & ILLEGAL_MASK )
/****************************************************************************
does a string have any lowercase chars in it?
****************************************************************************/
int strhaslower(const char *s)
{
while (*s) {
if (islower(*s))
return TRUE;
s++;
}
return FALSE;
}
/*******************************************************************
safe string copy into a known length string. maxlength does not
include the terminating zero.
********************************************************************/
char *safe_strcpy(char *dest,const char *src, size_t maxlength)
{
size_t len;
if (!dest) {
Syslog('+', "ERROR: NULL dest in safe_strcpy");
return NULL;
}
if (!src) {
*dest = 0;
return dest;
}
len = strlen(src);
if (len > maxlength) {
WriteError("ERROR: string overflow by %d in safe_strcpy [%.50s]", (int)(len-maxlength), src);
len = maxlength;
}
memcpy(dest, src, len);
dest[len] = 0;
return dest;
}
/* ************************************************************************** **
* Initialize the static character test array.
*
* Input: none
*
* Output: none
*
* Notes: This function changes (loads) the contents of the <chartest>
* array. The scope of <chartest> is this file.
*
* ************************************************************************** **
*/
static void init_chartest( void )
{
char *illegalchars = (char *)"*\\/?<>|\":";
unsigned char *s;
memset( (char *)chartest, '\0', 256 );
for( s = (unsigned char *)illegalchars; *s; s++ )
chartest[*s] = ILLEGAL_MASK;
for( s = (unsigned char *)basechars; *s; s++ )
chartest[*s] |= BASECHAR_MASK;
ct_initialized = TRUE;
} /* init_chartest */
/* ************************************************************************** **
* Return True if a name is a special msdos reserved name.
*
* Input: fname - String containing the name to be tested.
*
* Output: True, if the name matches one of the list of reserved names.
*
* Notes: This is a static function called by is_8_3(), below.
*
* ************************************************************************** **
*/
static int is_reserved_msdos( char *fname )
{
char upperFname[13];
char *p;
strncpy (upperFname, fname, 12);
/* lpt1.txt and con.txt etc are also illegal */
p = strchr(upperFname,'.');
if (p)
*p = '\0';
tu(upperFname);
p = upperFname + 1;
switch (upperFname[0]) {
case 'A':
if( 0 == strcmp( p, "UX" ) )
return TRUE;
break;
case 'C':
if ((0 == strcmp( p, "LOCK$" )) || (0 == strcmp( p, "ON" )) || (0 == strcmp( p, "OM1" ))
|| (0 == strcmp( p, "OM2" )) || (0 == strcmp( p, "OM3" )) || (0 == strcmp( p, "OM4" )))
return TRUE;
break;
case 'L':
if( (0 == strcmp( p, "PT1" )) || (0 == strcmp( p, "PT2" )) || (0 == strcmp( p, "PT3" )))
return TRUE;
break;
case 'N':
if( 0 == strcmp( p, "UL" ) )
return TRUE;
break;
case 'P':
if( 0 == strcmp( p, "RN" ) )
return TRUE;
break;
}
return FALSE;
} /* is_reserved_msdos */
/* ************************************************************************** **
* Return True if the name is a valid DOS name in 8.3 DOS format.
*
* Input: fname - File name to be checked.
* check_case - If True, then the
* name will be checked to see if all characters
* are the correct case.
*
* Output: True if the name is a valid DOS name, else FALSE.
*
* ************************************************************************** **
*/
int is_8_3( char *fname)
{
int len;
int l, i;
char *p;
char *dot_pos;
char *slash_pos = strrchr( fname, '/' );
/* If there is a directory path, skip it. */
if (slash_pos)
fname = slash_pos + 1;
len = strlen(fname);
/* Can't be 0 chars or longer than 12 chars */
if ((len == 0) || (len > 12))
return FALSE;
/* Mustn't be an MS-DOS Special file such as lpt1 or even lpt1.txt */
if (is_reserved_msdos(fname))
return FALSE;
init_chartest();
for (i = 0; i < strlen(fname); i++) {
if (isillegal(fname[i])) {
Syslog('+', "Illegal character in filename");
return FALSE;
}
}
/* Can't contain invalid dos chars */
p = fname;
dot_pos = NULL;
while (*p) {
if (*p == '.' && !dot_pos)
dot_pos = (char *)p;
p++;
}
/* no dot and less than 9 means OK */
if (!dot_pos)
return (len <= 8);
l = PTR_DIFF(dot_pos, fname);
/* base must be at least 1 char except special cases . and .. */
if (l == 0)
return(0 == strcmp( fname, "." ) || 0 == strcmp( fname, ".." ));
/* base can't be greater than 8 */
if (l > 8)
return FALSE;
if (len - l == 1 && !strchr( dot_pos + 1, '.' )) {
*dot_pos = 0;
return TRUE;
}
/* extension must be between 1 and 3 */
if ((len - l < 2 ) || (len - l > 4))
return FALSE;
/* extensions may not have a dot */
if (strchr( dot_pos+1, '.' ))
return FALSE;
/* must be in 8.3 format */
return TRUE;
}
/*****************************************************************************
* do the actual mangling to 8.3 format
* the buffer must be able to hold 13 characters (including the null)
*****************************************************************************
*/
void mangle_name_83(char *s)
{
int crc16, i;
char *p, *q;
char extension[4];
char base[9];
int baselen = 0;
int extlen = 0;
extension[0] = 0;
base[0] = 0;
/*
* First, convert some common Unix extensions to extensions of 3
* characters. If none fits, don't change anything now.
* FIXME: should be in an external file.
*/
if (strcmp(q = s + strlen(s) - strlen(".tar.gz"), ".tar.gz") == 0) {
*q = '\0';
q = (char *)"tgz";
} else if (strcmp(q = s + strlen(s) - strlen(".tar.z"), ".tar.z") == 0) {
*q = '\0';
q = (char *)"tgz";
} else if (strcmp(q = s + strlen(s) - strlen(".tar.Z"), ".tar.Z") == 0) {
*q = '\0';
q = (char *)"taz";
} else if (strcmp(q = s + strlen(s) - strlen(".html"), ".html") == 0) {
*q = '\0';
q = (char *)"htm";
} else if (strcmp(q = s + strlen(s) - strlen(".shtml"), ".shtml") == 0) {
*q = '\0';
q = (char *)"stm";
} else if (strcmp(q = s + strlen(s) - strlen(".conf"), ".conf") == 0) {
*q = '\0';
q = (char *)"cnf";
} else if (strcmp(q = s + strlen(s) - strlen(".mpeg"), ".mpeg") == 0) {
*q = '\0';
q = (char *)"mpg";
} else if (strcmp(q = s + strlen(s) - strlen(".smil"), ".smil") == 0) {
*q = '\0';
q = (char *)"smi";
} else if (strcmp(q = s + strlen(s) - strlen(".perl"), ".perl") == 0) {
*q = '\0';
q = (char *)"pl";
} else if (strcmp(q = s + strlen(s) - strlen(".jpeg"), ".jpeg") == 0) {
*q = '\0';
q = (char *)"jpg";
} else if (strcmp(q = s + strlen(s) - strlen(".tiff"), ".tiff") == 0) {
*q = '\0';
q = (char *)"tif";
} else {
q = NULL;
}
if (q) {
/*
* Extension is modified, apply changes
*/
p = s + strlen(s);
*p++ = '.';
for (i = 0; i < strlen(q); i++)
*p++ = q[i];
*p++ = '\0';
}
/*
* Now start name mangling
*/
p = strrchr(s,'.');
if (p && (strlen(p+1) < (size_t)4)) {
int all_normal = (!strhaslower(p+1)); /* XXXXXXXXX */
if (all_normal && p[1] != 0) {
*p = 0;
crc16 = crc16xmodem(s, strlen(s));
*p = '.';
} else {
crc16 = crc16xmodem(s, strlen(s));
}
} else {
crc16 = crc16xmodem(s, strlen(s));
}
tu(s);
if (p) {
if (p == s)
safe_strcpy(extension, "___", 3);
else {
*p++ = 0;
while (*p && extlen < 3) {
if (*p != '.' )
extension[extlen++] = p[0];
p++;
}
extension[extlen] = 0;
}
}
p = s;
/*
* Changed to baselen 4, original this is 5.
* 24-11-2002 MB.
*/
while (*p && baselen < 4) {
if (*p != '.' )
base[baselen++] = p[0];
p++;
}
base[baselen] = 0;
if (crc16 > (MANGLE_BASE * MANGLE_BASE * MANGLE_BASE))
Syslog('!', "WARNING: mangle_name_83() crc16 overflow");
crc16 = crc16 % (MANGLE_BASE * MANGLE_BASE * MANGLE_BASE);
sprintf(s, "%s%c%c%c%c", base, magic_char,
mangle(crc16 / (MANGLE_BASE * MANGLE_BASE)), mangle(crc16 / MANGLE_BASE), mangle(crc16));
if ( *extension ) {
(void)strcat(s, ".");
(void)strcat(s, extension);
}
}
/*****************************************************************************
* Convert a filename to DOS format.
*
* Input: OutName - Source *and* destination buffer.
*
* NOTE that OutName must point to a memory space that
* is at least 13 bytes in size! That should always be
* the case of course.
*
* ****************************************************************************
*/
void name_mangle(char *OutName)
{
char *p;
p = xstrcpy(OutName);
/*
* check if it's already in 8.3 format
*/
if (!is_8_3(OutName)) {
mangle_name_83(OutName);
} else {
/*
* No mangling needed, convert to uppercase
*/
tu(OutName);
}
free(p);
}