/* namedos.c : NameSpace DOS Services, mars_nwe * * Adds DOS 8.3 alias generation/resolution for long filenames on DOS * namespace clients. This file was originally derived from nameos2.c, * but DOS needs synthetic aliases while OS/2 can address long names. */ #include "net.h" #include #include #include #include #include #ifndef LINUX #include #endif #include "nwfname.h" #include "nwvolume.h" #include "connect.h" #include "nwfile.h" #include "unxfile.h" #include "namedos.h" #if WITH_NAME_SPACE_CALLS #define MAX_NAME_DOS_CACHE 0 static int dos_is_alias_char(int c) { if (isalnum((unsigned char)c)) return(1); switch (c) { case '$': case '%': case '\'': case '-': case '_': case '@': case '~': case '`': case '!': case '(': case ')': case '{': case '}': case '^': case '#': case '&': return(1); default: return(0); } } static int dos_alias_char(int c) { if (c >= 'a' && c <= 'z') c -= ('a' - 'A'); if (dos_is_alias_char(c)) return(c); return('_'); } int dos_is_83_name(uint8 *name, int options) { uint8 *ss=name; int len=0; int pf=0; if (!name || !*name) return(0); if (!strcmp((char*)name, ".") || !strcmp((char*)name, "..")) return(1); for (; *ss; ss++){ if (*ss == '.') { if (pf++) return(0); /* no second dot */ if (!len) return(0); /* no empty base */ len=0; } else { if (!dos_is_alias_char(*ss)) return(0); ++len; if ((pf && len > 3) || len > 8) return(0); if (!(options & VOL_OPTION_IGNCASE)){ if (options & VOL_OPTION_DOWNSHIFT){ if (*ss >= 'A' && *ss <= 'Z') return(0); } else { if (*ss >= 'a' && *ss <= 'z') return(0); } } } } return(len > 0); } static void build_dos_83_raw(uint8 *real_name, uint8 *alias, int alias_len) { uint8 base[9]; uint8 ext[4]; uint8 *dot=NULL; uint8 *s; int bl=0, el=0; memset(base, 0, sizeof(base)); memset(ext, 0, sizeof(ext)); if (!real_name || !*real_name) { strmaxcpy(alias, "_", alias_len-1); return; } if (real_name[0] != '.') dot=(uint8*)strrchr((char*)real_name, '.'); for (s=real_name; *s && s != dot; s++) { if (bl < 8) base[bl++] = (uint8)dos_alias_char(*s); } if (!bl) base[bl++] = '_'; if (dot) { for (s=dot+1; *s; s++) { if (el < 3) ext[el++] = (uint8)dos_alias_char(*s); } } if (el) snprintf((char*)alias, alias_len, "%s.%s", base, ext); else snprintf((char*)alias, alias_len, "%s", base); } static int alias_base_equal(uint8 *a, uint8 *b) { uint8 aa[14], bb[14]; uint8 *pa, *pb; strmaxcpy(aa, a, sizeof(aa)-1); strmaxcpy(bb, b, sizeof(bb)-1); pa=(uint8*)strchr((char*)aa, '.'); pb=(uint8*)strchr((char*)bb, '.'); if (pa) *pa='\0'; if (pb) *pb='\0'; return(!strcmp((char*)aa, (char*)bb)); } static char dos_digit(int n) { n &= 0x0f; return((n < 10) ? ('0' + n) : ('A' + n - 10)); } static void apply_collision_number(uint8 *alias, int collision) { uint8 *dot=(uint8*)strchr((char*)alias, '.'); uint8 ext[5]; uint8 base[9]; int bl; memset(ext, 0, sizeof(ext)); memset(base, 0, sizeof(base)); if (dot) { strmaxcpy(ext, dot, sizeof(ext)-1); *dot='\0'; } strmaxcpy(base, alias, sizeof(base)-1); bl=strlen((char*)base); if (bl >= 8) bl=7; base[bl++] = dos_digit(collision); base[bl] = '\0'; snprintf((char*)alias, 14, "%s%s", base, ext); } int build_dos_83_alias(int options, uint8 *parent_unix, uint8 *real_name, ino_t inode, uint8 *alias, int alias_len) { uint8 raw[14]; int collisions=0; DIR *d; (void)inode; if (alias_len < 2) return(0); *alias='\0'; if (dos_is_83_name(real_name, options)) { strmaxcpy(alias, real_name, alias_len-1); up_fn(alias); return(strlen((char*)alias)); } build_dos_83_raw(real_name, raw, sizeof(raw)); strmaxcpy(alias, raw, alias_len-1); if (parent_unix && *parent_unix && NULL != (d=opendir((char*)parent_unix))) { struct dirent *de; while ((de=readdir(d)) != NULL) { uint8 other[14]; if (!de->d_ino) continue; if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..")) continue; if (!strcmp(de->d_name, (char*)real_name)) continue; build_dos_83_raw((uint8*)de->d_name, other, sizeof(other)); if (!strcmp((char*)other, (char*)raw)) { if (strcmp(de->d_name, (char*)real_name) < 0) collisions++; } /* A real 8.3 name that equals the raw alias consumes that alias. */ if (dos_is_83_name((uint8*)de->d_name, options)) { uint8 native[14]; strmaxcpy(native, (uint8*)de->d_name, sizeof(native)-1); up_fn(native); if (!strcmp((char*)native, (char*)raw)) collisions++; } } closedir(d); } if (collisions > 0) { strmaxcpy(alias, raw, alias_len-1); apply_collision_number(alias, collisions-1); } up_fn(alias); return(strlen((char*)alias)); } static int dos_alias_match(uint8 *a, uint8 *b) { uint8 aa[14], bb[14]; strmaxcpy(aa, a, sizeof(aa)-1); strmaxcpy(bb, b, sizeof(bb)-1); up_fn(aa); up_fn(bb); return(!strcmp((char*)aa, (char*)bb)); } void mangle_dos_name(NW_VOL *vol, uint8 *unixname, uint8 *pp, int len) { uint8 wanted[14]; uint8 rest[512]; uint8 *slash; uint8 *parent_slash; DIR *d; if (!vol || !unixname || !pp || !*pp) return; if (len <= 1) return; slash=(uint8*)strchr((char*)pp, '/'); memset(rest, 0, sizeof(rest)); if (slash) { strmaxcpy(rest, slash, sizeof(rest)-1); *slash='\0'; } strmaxcpy(wanted, pp, sizeof(wanted)-1); up_fn(wanted); parent_slash = (pp > unixname && *(pp-1) == '/') ? pp-1 : NULL; if (parent_slash) *parent_slash='\0'; d=opendir((char*)unixname); if (d) { struct dirent *de; while ((de=readdir(d)) != NULL) { uint8 alias[14]; if (!de->d_ino) continue; if (!strcmp(de->d_name, ".") || !strcmp(de->d_name, "..")) continue; build_dos_83_alias(vol->options, unixname, (uint8*)de->d_name, de->d_ino, alias, sizeof(alias)); if (dos_alias_match(alias, wanted)) { if (parent_slash) *parent_slash='/'; snprintf((char*)pp, len, "%s%s", de->d_name, rest); closedir(d); if (*rest) { uint8 *next=(uint8*)strchr((char*)pp, '/'); if (next && *(next+1)) mangle_dos_name(vol, unixname, next+1, len - (int)((next+1) - pp)); } return; } } closedir(d); } if (parent_slash) *parent_slash='/'; if (slash) *slash='/'; } static inline int get_n_p(uint8 **p) { int pc=**p; (*p)++; if (pc == '\\') { pc=**p; (*p)++; } else if (pc == 255) { pc=**p; (*p)++; switch (pc) { case 0xaa : case '*' : return(3000); /* star */ case 0xae : case '.' : return(1000); /* point */ case 0xbf : case '?' : return(2000); /* ? */ default : break; } } return(pc); } static int ns_fn_dos_match(uint8 *s, uint8 *p, int soptions) /* OS/2 name matching routine */ { int pc, sc; uint state = 0; int anf, ende; int not = 0; uint found = 0; while ( (pc = get_n_p(&p)) != 0) { if (!(soptions & VOL_OPTION_IGNCASE)) { if (soptions & VOL_OPTION_DOWNSHIFT){ /* only downshift chars */ if (*s >= 'A' && *s <= 'Z') return(0); } else { if (*s >= 'a' && *s <= 'z') return(0); } } switch (state){ case 0 : switch (pc) { case '.' : case 1000: if (*s && ('.' != *s++)) return(0); break; case '?' : case 2000: if (!*s) return(0); ++s; break; case '*' : case 3000: { uint8 *pp; int np; if (!*p) return(1); /* last star */ while (*s) { if (ns_fn_dos_match(s, p, soptions) == 1) return(1); else if (*s=='.') { pp=p; if (!get_n_p(&p) || !get_n_p(&p)) return(0); p=pp; } ++s; } pp=p; np=get_n_p(&p); p=pp; if (np == '.' || np == 1000) return(ns_fn_dos_match(s, p, soptions)); } return(0); case '[' : if ( (*p == '!') || (*p == '^') ){ ++p; not = 1; } state = 1; continue; default : if (soptions & VOL_OPTION_IGNCASE) { if (!dfn_imatch(*s, pc)) return(0); } else if (pc != *s) return(0); ++s; break; } /* switch */ break; case 1 : /* Bereich von Zeichen */ sc = *s++; found = not; if (!sc) return(0); do { if (pc == '\\') pc = *(p++); if (!pc) return(0); anf = pc; if (*p == '-' && *(p+1) != ']'){ ende = *(++p); p++; } else ende = anf; if (found == not) { /* only if not found */ if (anf == sc || (anf <= sc && sc <= ende)) found = !not; } } while ((pc = *(p++)) != ']'); if (! found ) return(0); not = 0; found = 0; state = 0; break; default : break; } /* switch */ } /* while */ if (*s=='.' && *(s+1)=='\0') return(1); return ( (*s) ? 0 : 1); } #endif