U8 *FileExtDot(U8 *src)
{//Find dot char in name.
  I64 ch;
  while (ch=*src++)
    if (ch=='.' && *src!='/' && *src!='.')
      return src-1;
  return NULL;
}

U8 *FileExtRem(U8 *src,U8 *dst=NULL)
{//Remove filename extension from str.
  U8 *ptr;
  if (ptr=FileExtDot(src)) {
    if (dst)
      StrCpy(dst,ptr+1);
    *ptr=0;
  } else if (dst)
    *dst=0;
  return dst;
}

Bool IsDotZ(U8 *filename)
{//Does name end in .Z?
  I64 i=StrLen(filename);
  if (StrOcc(filename,'.')>1 && filename[i-1]=='Z' && filename[i-2]=='.')
    return TRUE;
  else
    return FALSE;
}

Bool IsDotC(U8 *filename)
{//Does name end in .C?
  I64 i=StrLen(filename);
  if (StrOcc(filename,'.')>1 && filename[i-1]=='C' && filename[i-2]=='.')
    return TRUE;
  else
    return FALSE;
}

Bool FilesFindMatch(U8 *_test_name,U8 *files_find_mask,I64 fuf_flags=0)
{//Does filename meet Files Find mask?
  I64 tn_len=StrLen(_test_name),mask_len=StrLen(files_find_mask);
  U8 *mask1=MAlloc(mask_len+1),*mask2=MAlloc(mask_len+1),
        *ptr,*test_name1,*test_name2;
  Bool res=FALSE;
  StrCpy(mask1,files_find_mask);
  if (StrOcc(_test_name,'/')) {
    test_name1=MAlloc(tn_len+1);
    test_name2=MAlloc(tn_len+1);
    StrCpy(test_name1,_test_name);
    StrLastRem(test_name1,"/",test_name2);
  } else {
    test_name1=NULL;
    test_name2=NULL;
  }
  while (TRUE) {
    StrFirstRem(mask1,";",mask2);
    if (!test_name2 || StrOcc(mask2,'/'))
      ptr=_test_name;
    else
      ptr=test_name2;
    if (*mask2) {
      if (*mask2=='!') {
        if (WildMatch(ptr,mask2+1)) {
          res=FALSE;
          break;
        }
      } else {
        if (WildMatch(ptr,mask2)) {
          if (Bt(&fuf_flags,FUf_JUST_TXT) &&
                !FilesFindMatch(_test_name,FILEMASK_TXT)) {
            res=FALSE;
            break;
          } else if (Bt(&fuf_flags,FUf_JUST_DD) &&
                !FilesFindMatch(_test_name,FILEMASK_DD)) {
            res=FALSE;
            break;
          } else if (Bt(&fuf_flags,FUf_JUST_SRC) &&
                !FilesFindMatch(_test_name,FILEMASK_SRC)) {
            res=FALSE;
            break;
          } else if (Bt(&fuf_flags,FUf_JUST_AOT) &&
                !FilesFindMatch(_test_name,FILEMASK_AOT)) {
            res=FALSE;
            break;
          } else if (Bt(&fuf_flags,FUf_JUST_JIT) &&
                !FilesFindMatch(_test_name,FILEMASK_JIT)) {
            res=FALSE;
            break;
          } else if (Bt(&fuf_flags,FUf_JUST_GR) &&
                !FilesFindMatch(_test_name,FILEMASK_GR)) {
            res=FALSE;
            break;
          } else
            res=TRUE;
        }
      }
    } else
      break;
  }
  Free(test_name1);
  Free(test_name2);
  Free(mask1);
  Free(mask2);
  return res;
}

U8 *DirNameAbs(U8 *_dirname)
{//MAlloc absolute dir string with drv letter.
  I64 maxlen;
  U8 drv[3],*res,*buf,*buf2,*buf3,*buf4,*dirname,*free_dirname;
  if (!Fs->cur_dir || !*Fs->cur_dir)
    return StrNew(_dirname);
  free_dirname=dirname=MStrUtil(_dirname,
        SUF_REM_LEADING|SUF_REM_TRAILING|SUF_REM_CTRL_CHARS);
  *drv=Drv2Let;
  drv[1]=':';
  drv[2]=0;
  if (*dirname && dirname[1]==':') {
    if (*dirname==':')
      *drv=blkdev.boot_drv_let;
    else if (*dirname=='~')
      *drv=*blkdev.home_dir;
    else
      *drv=*dirname;
    dirname=dirname+2;
    buf=StrNew("/");
  } else
    buf=StrNew(Fs->cur_dir);
  if (*dirname=='/') {
    Free(buf);
    buf=StrNew("/");
    dirname++;
  }
  buf2=StrNew(dirname);
  maxlen=StrLen(buf)+1+StrLen(buf2)+1;
  buf3=MAlloc(maxlen);
  buf4=MAlloc(maxlen);
  StrCpy(buf3,buf);
  while (*buf2) {
    StrFirstRem(buf2,"/",buf4);
    if (!*buf4)
      StrCpy(buf3,"/");
    else if (!StrCmp(buf4,"..")) {
      StrLastRem(buf3,"/");
      if (!*buf3)
        StrCpy(buf3,"/");
    } else if (!StrCmp(buf4,"~")) {
      Free(buf3);
      buf3=MAlloc(StrLen(blkdev.home_dir+2)+1+StrLen(buf2)+1);
      StrCpy(buf3,blkdev.home_dir+2);
      *drv=*blkdev.home_dir;
    } else if (!StrCmp(buf4,"."));
    else if (*buf4) {
      if (StrCmp(buf3,"/"))
        CatPrint(buf3,"/");
      CatPrint(buf3,buf4);
    }
  }
  Free(buf);
  res=MAlloc(StrLen(buf3)+3);
  StrCpy(res,drv);
  StrCpy(res+2,buf3);
  Free(buf2);
  Free(buf3);
  Free(buf4);
  Free(free_dirname);
  return res;
}

U8 *FileNameAbs(U8 *_filename,I64 fuf_flags=0)
{//Absolute filename. Accepts FUF_Z_OR_NOT_Z, FUF_SCAN_PARENTS.
  U8 *res,*filename,*buf,*buf_file,*buf_dir,*free_filename,*free_buf;
  CDirEntry de;
  free_filename=filename=MStrUtil(_filename,
        SUF_REM_LEADING|SUF_REM_TRAILING|SUF_REM_CTRL_CHARS);
  free_buf=buf=StrNew(filename);
  if (*buf && buf[1]==':') {
    buf+=2;
    filename+=2;
  }
  buf_file=MAlloc(StrLen(free_filename)+1);
  StrLastRem(buf,"/",buf_file);
  if (*filename=='/' && !*buf)
    StrCpy(buf,"/");
  buf_dir=DirNameAbs(free_buf);
  Free(free_buf);
  res=MAlloc(StrLen(buf_dir)+1+StrLen(buf_file)+1);
  StrCpy(res,buf_dir);
  if (res[StrLen(res)-1]!='/')
    CatPrint(res,"/");
  CatPrint(res,buf_file);
  Free(buf_file);
  Free(buf_dir);
  Free(free_filename);
  if (fuf_flags && FileFind(res,&de,fuf_flags|FUF_JUST_FILES)) {
    Free(res);
    res=de.full_name;
  }
  return res;
}

U8 *ExtChg(U8 *filename,U8 *extension)
{//Change filename extension.
  U8 *res=MAlloc(StrLen(filename)+1+StrLen(extension)+1);
  StrCpy(res,filename);
  if (FileExtDot(filename))
    FileExtRem(res);
  return CatPrint(res,".%s",extension);
}

U8 *ExtDft(U8 *filename,U8 *extension)
{//Give extension if has none.
  U8 *res=MAlloc(StrLen(filename)+1+StrLen(extension)+1);
  StrCpy(res,filename);
  if (!FileExtDot(filename))
    CatPrint(res,".%s",extension);
  return res;
}

CDirEntry *Cd2DirEntry(CDirEntry *tmpde,U8 *abs_name)
{
  I64 i;
  while (tmpde) {
    i=StrLen(tmpde->full_name);
    if (StrNCmp(tmpde->full_name,abs_name,i)||
          i && tmpde->full_name[i-1]!='/' && abs_name[i] && abs_name[i]!='/')
      tmpde=tmpde->next;
    else
      if (StrLen(abs_name)==i)
        return tmpde;
      else
        return Cd2DirEntry(tmpde->sub,abs_name);
  }
  return NULL;
}

I64 FileAttr(U8 *name,I64 attr=0)
{
  if (IsDotZ(name))
    attr|=RS_ATTR_COMPRESSED;
  else
    attr&=~RS_ATTR_COMPRESSED;
  if (IsDotC(name))
    attr|=RS_ATTR_CONTIGUOUS;
  return attr;
}

Bool FileNameChk(U8 *filename)
{//Return check for valid filename, not checking existence.
  U8 *ptr=filename;
  if (!filename) return FALSE;
  if (!*ptr) return FALSE;
  if (*ptr=='.') {
    if (!ptr[1]) return TRUE;
    if (ptr[1]=='.' && !ptr[2]) return TRUE;
  }
  if (StrLen(filename)>=CDIR_FILENAME_LEN) return FALSE;
  while (*ptr)
    if (!Bt(char_bmp_filename,*ptr++))
      return FALSE;
  return TRUE;
}

U8 *ToggleZorNotZ(U8 *name)
{//MAlloc name with Z if not Z or vice versa.
  U8 *res;
  if (IsDotZ(name)) {
    res=StrNew(name);
    res[StrLen(name)-2]=0;
  } else
    res=MStrPrint("%s.Z",name);
  return res;
}

U8 *FileNameTmpTxt()
{//Make pretty-safe tmp filename in home dir.
  return MStrPrint("~/SysTmp%X.DD.Z",GetTSC>>8&0xFFFFFFFF);
}

U8 *DirCur(CTask *task=NULL,CTask *mem_task=NULL)
{//MAlloc copy of cur dir with drv letter.
  U8 *st;
  if (!task) task=Fs;
  if (!task->cur_dir)
    return NULL;
  st=MAlloc(StrLen(task->cur_dir)+3,mem_task);
  *st=Drv2Let(task->cur_dv);
  st[1]=':';
  StrCpy(st+2,task->cur_dir);
  return st;
}

U8 *DirFile(U8 *dirname,U8 *name=NULL,U8 *_extension=NULL)
{/*Strips file from dirname, scans for file upward until found or
returns default.

("/Kernel/KHashA.HC.Z",NULL,NULL)       returns "D:/Kernel"
("/Kernel",NULL,"PRJ.Z")                returns "D:/Kernel/Kernel.PRJ.Z"
("/Kernel/BlkDev",NULL,"PRJ.Z")         returns "D:/Kernel/Kernel.PRJ.Z"
("/Apps/Psalmody","Load","HC.Z")        returns "D:/Apps/Psalmody/Load.HC.Z"
*/
  U8 *st=DirNameAbs(dirname),*st2,*st3,*res,*dft=NULL,*ext;
  if (_extension && *_extension) {
    if (*_extension=='.')
      ext=StrNew(_extension);
    else
      ext=MStrPrint(".%s",_extension);
  } else
    ext=StrNew("");
  while (StrOcc(st,'/')&&!IsDir(st))
    StrLastRem(st,"/");
  while (StrOcc(st,'/')) {
    st2=StrNew(st);
    st3=StrNew(st);
    StrLastRem(st2,"/",st3);

    if (name)
      res=MStrPrint("%s/%s%s",st,name,ext);
    else {
      if (*ext)
        res=MStrPrint("%s/%s%s",st,st3,ext);
      else
        res=StrNew(st);
    }
    if (!dft)
      dft=StrNew(res);
    if (!*ext && (!name||!*name) || FileFind(res)) {
      Free(st3);
      Free(st2);
      Free(st);
      Free(dft);
      Free(ext);
      return res;
    }
    Free(st);
    st=st2;
    Free(st3);
  }
  Free(st);
  Free(ext);
  return dft;
}