/*
 * This file is part of OpenModelica.
 *
 * Copyright (c) 1998-2026, Open Source Modelica Consortium (OSMC),
 * c/o Linköpings universitet, Department of Computer and Information Science,
 * SE-58183 Linköping, Sweden.
 *
 * All rights reserved.
 *
 * THIS PROGRAM IS PROVIDED UNDER THE TERMS OF AGPL VERSION 3 LICENSE OR
 * THIS OSMC PUBLIC LICENSE (OSMC-PL) VERSION 1.8.
 * ANY USE, REPRODUCTION OR DISTRIBUTION OF THIS PROGRAM CONSTITUTES
 * RECIPIENT'S ACCEPTANCE OF THE OSMC PUBLIC LICENSE OR THE GNU AGPL
 * VERSION 3, ACCORDING TO RECIPIENTS CHOICE.
 *
 * The OpenModelica software and the OSMC (Open Source Modelica Consortium)
 * Public License (OSMC-PL) are obtained from OSMC, either from the above
 * address, from the URLs:
 * http://www.openmodelica.org or
 * https://github.com/OpenModelica/ or
 * http://www.ida.liu.se/projects/OpenModelica,
 * and in the OpenModelica distribution.
 *
 * GNU AGPL version 3 is obtained from:
 * https://www.gnu.org/licenses/licenses.html#GPL
 *
 * This program is distributed WITHOUT ANY WARRANTY; without
 * even the implied warranty of MERCHANTABILITY or FITNESS
 * FOR A PARTICULAR PURPOSE, EXCEPT AS EXPRESSLY SET FORTH
 * IN THE BY RECIPIENT SELECTED SUBSIDIARY LICENSE CONDITIONS OF OSMC-PL.
 *
 * See the full OSMC Public License conditions for more details.
 *
 */

//
//   dirwalk.c
//
//   Adrian Pop
//   adrpo@ida.liu.se
//   2004-11-01
//   updates: 2006-12-15 added walking for Linux.
//                       this one is generic!
#include "dirwalk.h"

// windows part!
#if defined(__MINGW32__) || defined(_MSC_VER)

#include <windows.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <iostream>
#include <sstream>
#include <cstdlib>
#include <fstream>

#define PATH_MAX 2048

//------------------------------------------------
int getDirectoryStructure(char *_current, l_list &dirList, int _dlevel)
//------------------------------------------------
{
  char            DirName[PATH_MAX];
  static char     CurrDirName[PATH_MAX];
  HANDLE          Hnd;
  WIN32_FIND_DATA WFD;

  if (!_dlevel)
  {
    GetCurrentDirectory( PATH_MAX, CurrDirName );
    //std::cout << "Get:" << CurrDirName << std::endl;
  }

  //  Set the new current directory
  if (!SetCurrentDirectory( _current ))
  {
    std::cerr << "Error: could not open directory: " << _current << std::endl;
    exit(1);
  }

  //std::cout << "+" << _current << " + " << _dlevel << std::endl;

  //  Starts the search
  Hnd = FindFirstFile( "*.*", &WFD );

  if (!_dlevel)
  {
    GetCurrentDirectory( PATH_MAX, DirName );
    // add DirName to dirList
    char *tmpDir = new char[sizeof(char)*strlen(DirName)+1];
    strcpy(tmpDir, DirName);
    tmpDir[strlen(DirName)]='\0';
    dirList.push_back(tmpDir);
    //printf("%s\n", DirName);
  }

  //  loop to get all inside the current directory
  while ( FindNextFile( Hnd, &WFD ) )
  {
    //    If it is a real directory
    if (
      ( WFD.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY ) &&
      ( strcmp(WFD.cFileName, "..") && strcmp(WFD.cFileName, ".") )
      )
    {
      //       Get the current directory
      GetCurrentDirectory( PATH_MAX, DirName );

      //       Put a "\" if necessary
      if ( strncmp( &DirName[strlen(DirName)-1], PATH_SEPARATOR, 1 ) )
        (void) strcat( DirName, PATH_SEPARATOR );

      //       Create a new path
      (void) strcat( DirName, WFD.cFileName );

      //       Show the new directory
      // add DirName to dirList
      char *tmpDir = new char[sizeof(char)*strlen(DirName)+1];
      strcpy(tmpDir, DirName);
      tmpDir[strlen(DirName)]='\0';
      dirList.push_back(tmpDir);
      //printf("%s\n", DirName);

      //       Make a new call to itself
      getDirectoryStructure( DirName, dirList, ++_dlevel);

      //       Go back one level
      SetCurrentDirectory( ".." );

      _dlevel--;
    }

  } // End while

  // End the search to this call
  (void) FindClose( Hnd );
  if (!_dlevel)
  {
    SetCurrentDirectory( CurrDirName );
    //std::cout << "Set:" << CurrDirName << " + " << _dlevel << std::endl;
  }
  return 1;
}

//-------------------------------------------------------------------------
int getFileList(char *currentDir, l_list &fileList, char* fileFilter)
//-------------------------------------------------------------------------
{
  char            CurrDirName[PATH_MAX];
  HANDLE          Hnd;
  WIN32_FIND_DATA WFD;
  int fileNo = 0;

  GetCurrentDirectory( PATH_MAX, CurrDirName );

  //  Set the new current directory
  SetCurrentDirectory( currentDir );

  //  Starts the search
  Hnd = FindFirstFile( fileFilter, &WFD );

  if (Hnd == INVALID_HANDLE_VALUE)
  {
    SetCurrentDirectory( CurrDirName );
    return 0;
  }

  //  loop to get all inside the current directory
  do
  {
    //    If it is a real file
    if (
      (( WFD.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY )
      != FILE_ATTRIBUTE_DIRECTORY) &&
      ( strcmp(WFD.cFileName, "..") && strcmp(WFD.cFileName, ".") )
      )
    {
      // add filename to fileList
      char *tmpFile = new char[sizeof(char)*
             (strlen(WFD.cFileName) +
              strlen(PATH_SEPARATOR) +
              strlen(currentDir))+1];
      tmpFile[0] = '\0';
      strcat(tmpFile, currentDir);
      strcat(tmpFile, PATH_SEPARATOR);
      strcat(tmpFile, WFD.cFileName);
      fileList.push_back(tmpFile);
      fileNo++;
    }
  } // End while
  while ( FindNextFile( Hnd, &WFD ) );

  // End the search to this call
  (void) FindClose( Hnd );
  SetCurrentDirectory( CurrDirName );
  return fileNo;
}

bool endsWith ( std::string str, std::string suffix )
{
  std::string::size_type i = str.rfind( suffix );
  if (i == std::string::npos)
    return false;
  if (i == ( str.size() - suffix.size()))
    return true;
  return false;
}

#else /* Linux part! */

#include <iostream>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#include <ctype.h>
#include <sys/unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <dirent.h>
#include <sys/stat.h>
#include <sys/param.h> /* MAXPATHLEN */
#include <ftw.h>

#define DEPTH 8
#define ERROR -1
l_list dirListGlobal;

using namespace std;

bool endsWith ( std::string str, std::string suffix )
{
  std::string::size_type i = str.rfind( suffix );
  if (i == std::string::npos)
    return false;
  if (i == ( str.size() - suffix.size()))
    return true;
  return false;
}

void CheckErrNo ( const char * path )
{
  switch ( errno )     //  errno is a system global value
    {
    case  EFAULT  :   std::cout << "Path points outside your addr. space:  " << path << std::endl;
      break;

    case  EACCES  :   std::cout << "Access denied:  " << path << std::endl;
      break;

    case  EPERM   :   std::cout << "Permanent Entry:  " << path << std::endl;
      break;

    case  ENOENT  :   std::cout << path << " does not exist." << std::endl;
      break;

    case  EISDIR  :   std::cout << path << " refers to a directory." << std::endl;
      break;

    case  ENOMEM  :   std::cout << path << ".  Insufficient kernel memory. "
         << "Exiting..." << std::endl;
      exit ( ENOMEM );
      break;

    case  EROFS   :   std::cout << path << " is read only." << std::endl;
      break;

    case ENOTDIR  :   std::cout << path << ".  Component in path not a directory."
         << std::endl;
      break;
    }
}

//------------------------------------------------
int push_dir(const char *fpath, const struct stat *sb, int tflag, struct FTW *ftwbuf)
//------------------------------------------------
{
   int result = 0;
   switch ( tflag )
      {
      case FTW_F:       /* do nothing on file now. */
  break;

      case FTW_D:
      case FTW_DP:
  dirListGlobal.push_front ( strdup(fpath) );
  break;
      case FTW_DNR:
  std::cout << "Directory cannot be read:  "
       << fpath << std::endl;
  CheckErrNo ( fpath );
  break;


      case FTW_NS:
  std::cout << "Stat failure on:  "
       << fpath << std::endl;
  CheckErrNo ( fpath );
  break;

      default:
  std::cout << "Weird flag for file  " << fpath << std::endl;

      }
   return result;
}

//------------------------------------------------
int getDirectoryStructure(char *_current, l_list &dirList, int _dlevel)
//------------------------------------------------
{
  int flags = 0;
  flags |= FTW_DEPTH; /* handle the directory given first, then dive */
  flags |= FTW_PHYS;  /* do not folow symbolic links */
  dirListGlobal.clear(); /* clear the elements! */

  if ( nftw ( _current, push_dir, DEPTH, flags) == ERROR )
  {
      CheckErrNo ( _current );
  }
  dirList = dirListGlobal;

  return 1;
}

int file_select_mo(const struct dirent *entry)
{
  char fileName[MAXPATHLEN];
  int res; char* ptr;
  struct stat fileStatus;
  if ((strcmp(entry->d_name, ".") == 0) ||
      (strcmp(entry->d_name, "..") == 0)) {
    return (0);
  } else {
    ptr = (char*)rindex(entry->d_name, '.');
    if ((ptr != NULL) &&
  ((strcmp(ptr, ".mo") == 0))) {
      return (1);
    } else {
      return (0);
    }
  }
}

//-------------------------------------------------------------------------
int getFileList(char *currentDir, l_list &fileList, char* fileFilter)
//-------------------------------------------------------------------------
{
  struct dirent **namelist;
  int n;

  n = scandir(currentDir, &namelist, file_select_mo, NULL);
  if (n < 0)
    perror("scandir");
  else
  {
    for(int i = 0; i <n; i++)
    {
      char *tmpFile = new char[sizeof(char)*
             (strlen(namelist[i]->d_name) +
        strlen(PATH_SEPARATOR) +
        strlen(currentDir))+1];
      tmpFile[0] = '\0';
      strcat(tmpFile, currentDir);
      strcat(tmpFile, PATH_SEPARATOR);
      strcat(tmpFile, namelist[i]->d_name);
      fileList.push_back(tmpFile);
    }
    free(namelist);
  }
  return 1;
}

#endif
