// Nemytykh, Version 01-09-2025

//# include "cdecl.h"
//# include "cfunc.h"
# include "decl.h"
# include "fileio.h"
# include "ifunc.h"
# include <stdio.h>

#ifdef FOR_OS_WINDOWSNT
/* For _stat () */
#  include <sys/types.h>
#  include <sys/stat.h>
#  include <fcntl.h>
/* For _getcwd () */
#	include <direct.h>
#else
/* For stat () */
#	include <sys/stat.h>

/* For stat (), getcwd () */
#	include <unistd.h>

/* For 	WIFEXITED, WEXIATUS */
#	include <sys/types.h>
#endif

#ifdef BY_GCC_MINGW_WIN
  /* For the function ri_UserLocaleName */
# include <windows.h>
#endif


# include "utf8-vs-utf16.h"


# ifdef BY_GCC_MINGW_WIN
  	static wchar_t  **r_wargv, **r_wenpv;
	static int r_wargc, r_si;
# endif

/* Local functions. */
int r_sutf16_from_utf8 (wchar_t *, char *); 
int r_intWinUtf16_from_utf8symb (wchar_t * , utf);


// 77777, Nemytykh, 27-07-2025
  // https://github.com/coderforlife/mingw-unicode-main/
  // https://github.com/coderforlife/mingw-unicode-main/blob/master/mingw-unicode.c
  // https://github.com/coderforlife/mingw-unicode-main/blob/master/mingw-unicode-gui.c

/* Initialisation of the array containing the refgo/refc/reftr cmd-command-line argument values. */
void r_wargv_init(void) {
# ifdef BY_GCC_MINGW_WIN
   r_si = 0;
   __wgetmainargs(&r_wargc, &r_wargv, &r_wenpv, _CRT_glob, &r_si); // this also creates the global variable __wargv
# endif
   return;
}

/* Get the arv[i] value from the array mentioned above. */
char * r_wgetargvi(char * argv[], int i) {
# ifdef BY_GCC_MINGW_WIN
	return (char *)(r_wargv[i]);
# else
	return argv[i];
# endif
}


# ifdef BY_GCC_MINGW_WIN

/* Encode utf16-source string, including L'\0', in utf8-distination string, using utf16mode. 
   At present this function is used only for communication with the files system of the Windows OS
   rather than with file contents.
   I.e. it is used only for recognizing the file and directoty names and the enviromen-variable values.
   As a consequence, it ignores the last argumnent value, since the value is always ENC_UTF_16LE.
   And such a value (temporary) is considered as the default value.
*/
void r_sputws_enc_utf16_to_utf8 (char * dist, wchar_t * src, char utf16mode) {
	int unicode;
	utf symb;
	char * p;

		for (; *src != L'\0'; src++) {
	      if ( (unsigned int)(*src) < 128 ) { // ASCII byte.
	      	(*dist++) = (unsigned char)(*src);
	      }	else if ( ((unsigned int)(*src) < 0xD800) || ((unsigned int)(*src) > 0xDFFF) ) {
	         //  Regular utf-16 pairs 0-55295 and 57344-65535  , (FirstW < 0xD800) || (FirstW > 0xDFFF)
	      	symb = r_int_to_utf((unsigned int)(*src));
	      	p = r_utf_expl(symb);
	      	while (*p) { *(dist++) = *(p++); }
	      }	else {
             /* Surrogate pairs are considered below. */
                 //	Left surrogate symbol: *src
             unicode = (int)((wchar_t)( *(src++) ) & 0x3ff) << 10;
                 //	Right surrogate symbol: *src
             unicode = unicode | (int)((wchar_t)(*src) & 0x3ff);
             symb = r_int_to_utf((unsigned int)(unicode) + 0x10000);
             p = r_utf_expl(symb);
	      	 while (*p) { *(dist++) = *(p++); }
	      };
		};
		*dist = '\0';
		return;
}
# endif


/* Recoding the * argv[i] string-value from utf-16 to utf-8 dist. */
int r_getstr_from_mainargvi (char * dist, char * argv[], int i) {

# ifdef BY_GCC_MINGW_WIN
      char utf16mode = ENC_UTF_16LE; 
      r_sputws_enc_utf16_to_utf8 (dist, r_wargv [i], utf16mode);
# else
		strcpy ( dist, argv [i] );
# endif
      return 0;
}


# ifdef BY_GCC_MINGW_WIN

/* This function returns 1 iff the variable envarn does exist in the enviroment.
   In such a case the variable value is copied in destination (dest).
   Otherwise the function returns -1.
*/ 
// int r_getenv_from_mainargvi (char * dist, char * envarn) {
int r_getenv_from_mainargvi (char * envarn) {
	int res = -1;
	char * utf8str, * qn, * qu;
	char * dist = envarn;

     if( NULL == (qu = (char *) malloc (ENV_VARNAME_MAX+ENV_VARVALUE_MAX+3)) ) {
		  	fprintf (stderr, "No memory for value of the enviroment variable \'%s\'.\n",envarn);
  			exit (1);
     }

   	char utf16mode = ENC_UTF_16LE; 
   	for(int j = 0; r_wenpv[j] != NULL; j++) {
         utf8str = qu;
   		r_sputws_enc_utf16_to_utf8 (utf8str, r_wenpv[j], utf16mode);
         qn = envarn;
   		while ( 1 ) {
   			if ( (*qn != * utf8str) ) {
   				if ( (* utf8str == '=') ) { /* the name of the env-variable has been found, */
   					strcpy(dist,utf8str+1);  /* copy its value  */
   					res = 1;
   					goto end;
   				} else break;
   			}
   			qn++;
   			utf8str++;
   		};
   	}
   	end:
   	  free (qu);
   	  return res;
}
# endif

# ifdef BY_GCC_MINGW_WIN
static char r_cmdarg[512];
# endif

/* Get the n-th refal-command-line argument into the destination. */
char * r__wgetarg (char * dist, int n) {

# ifdef BY_GCC_MINGW_WIN
   	char utf16mode = ENC_UTF_16LE; 
   	wchar_t * p = (wchar_t *)r_wargv[n];
   	
  		r_sputws_enc_utf16_to_utf8 ((char *)r_cmdarg, p, utf16mode);
      dist = (char *)r_cmdarg;
# endif
      return dist;
}

/* Get the current working-directory path into the buffer ca_curdir. */
char * r_getcwd (char * ca_curdir, int max_path_len) {

# ifdef BY_GCC_MINGW_WIN
  	char utf16mode = ENC_UTF_16LE; 
   wchar_t * p = malloc(max_path_len);

	if ((p == NULL) || (NULL == _wgetcwd (p, max_path_len))) 
		{ return NULL; }
  	r_sputws_enc_utf16_to_utf8 (ca_curdir, p, utf16mode);
   free(p);
# else
	if ( NULL == getcwd (ca_curdir, max_path_len) ) 
		return NULL;
# endif
   return ca_curdir;
}


/* Get pointer to the value of the input enviroment varaiable envarn.  */
char * r_wgetenv (char * envarn) {
	char * p;
	
# ifdef BY_GCC_MINGW_WIN
      if( r_getenv_from_mainargvi (envarn) == 1 ) {
      	p = envarn;
      } else {
      	p = NULL;
      }
# else
      p = getenv (envarn);
# endif
   return p;
}


# if defined BY_GCC_MINGW_WIN || defined FOR_OS_LINUX 

/* See comments to the function r_sputws_enc_utf16_to_utf8. */
int r_sutf16_from_utf8 (wchar_t * dist, char * src) {
	int i;
	utf usymb;

		while (* src != '\0') {
		   if ( * src < 128) {
		   	* dist++ = (wchar_t)(* src++);
		   } else {
		      usymb = r_isunicode(src);
            i = r_get_utf_ln();
            if ( !usymb ) return -1;
	         r_intWinUtf16_from_utf8symb(dist, usymb);
            src += (i ? i : 1);
            while ( * dist != L'\0' ) dist++;
		   };
		};
		* dist = L'\0';
		return 0;
}


// Еще один входной аргумент должен быть: ENC_UTF_16LE или ENC_UTF_16BE? 
//   -- Здесь явно в названии указан Win ...
/* See comments to the function r_sputws_enc_utf16_to_utf8. */
int r_intWinUtf16_from_utf8symb (wchar_t * utf16, utf symb) {
   unsigned int u_ord;
   
      if ( symb > 127 ) {
         u_ord = ri_utf_to_int(symb); 
      } else {
        * utf16++ = symb; 
        goto outres;
      };
      if ( (u_ord >= 0x10000) && (u_ord <= 0x10FFFF) ) { // Surrogate pairs
        u_ord -= 0x10000;
        unsigned short shortLo = ( (unsigned short)(u_ord & 0x3FF) | 0xDC00 ); 
        unsigned short shortHi = ( (unsigned short)(u_ord >> 10) | 0xD800 ); 
        * utf16++ = shortHi; 
        * utf16++ = shortLo; 
      } else {
        * utf16++ = u_ord;
      }
   outres:  
	   * utf16 = L'\0';
	   return 0;
}


# endif

# define RF_REFC  1
# define RF_REFGO 2

FILE * rc_wfopen (char * fname, char * mode) {
   FILE * fp = r_wfopen (fname, mode);
   
	if ((fp != NULL) && (tolower(*(mode)) == 'r') && (tolower(*(mode+1)) != 'b')) {
		if ( r_check_utf_BOM(fp, ENC_UTF_8, RF_REFC) == 1 ) {
      	fprintf(stderr,"\nSee the file %s\n\n",fname); // It is a warning rather than an error.
		}
	}
	return fp; 
}

FILE * ri_wfopen (char * fname, char * mode, int enc) {
   FILE * fp = r_wfopen (fname, mode);

	if ((fp != NULL) && (tolower(*(mode)) == 'r') && (tolower(*(mode+1)) != 'b')) {
		if ( r_check_utf_BOM(fp, enc, RF_REFGO) == 1 ) {
      	fprintf(stderr,"\nSee the file %s\n\n",fname); // It is a warning rather than an error.
		}
	}
	return fp; 
}

FILE * r_wfopen (char * fname, char * mode) {
   
# ifdef BY_GCC_MINGW_WIN
  	wchar_t encUTF16_fname [FILENAME_MAX], encUTF16_mode [6];
   
   r_sutf16_from_utf8 (encUTF16_fname, fname);
   r_sutf16_from_utf8 (encUTF16_mode, mode);
	return _wfopen (encUTF16_fname, encUTF16_mode);
# else
	return fopen (fname, mode); 
# endif
}

int r_wunlink (char * fname) {
# ifdef BY_GCC_MINGW_WIN
  	wchar_t encUTF16_fname [FILENAME_MAX];
   
   r_sutf16_from_utf8 (encUTF16_fname, fname);
	return _wunlink (encUTF16_fname);
# else
	return unlink (fname); 
# endif
}

int r_wsystem (char * utf8str) {
# ifdef BY_GCC_MINGW_WIN
	wchar_t utf16str [MAX_SYSTEM_CALL+2];
	r_sutf16_from_utf8 (utf16str, utf8str);
	return _wsystem (utf16str);
# else
	return system (utf8str); 
# endif
}

int r_fremove (char * utf8str) {
# ifdef BY_GCC_MINGW_WIN
	wchar_t utf16str [FILENAME_MAX+2];
	r_sutf16_from_utf8 (utf16str, utf8str);
	return _wremove (utf16str);
# else
	return remove (utf8str); 
# endif
}

int r_stat (char * utf8str, struct stat * st_bufptr) {
// # if defined BY_GCC_MINGW_WIN || defined FOR_OS_LINUX 
# if defined BY_GCC_MINGW_WIN 
	wchar_t utf16str [FILENAME_MAX+2];
	r_sutf16_from_utf8 (utf16str, utf8str);
	return wstat (utf16str, st_bufptr);
# else
	return stat (utf8str, st_bufptr); 
# endif	
}

                       //   The unicode point U+FEFF :
# define RF_UTF8_BOM    "8 (0xEFBBBF = 239187191)"
# define RF_UTF16BE_BOM "16BE (0xFEFF = 254255)"
# define RF_UTF16LE_BOM "16LE (0xFFFE = 255254)"
//# define RF_UTF16LE_BOM "UTF16LE-BOM (0xFFFE = 255254)"
# define RF_UTF32BE_BOM "32BE (0x0000FEFF = 00254255)"
# define RF_UTF32LE_BOM "32LE (0xFFFE0000 = 25425500)"

# define RF_NO_BOM ""
#include <string.h>

static int r_bom_warning_st;

int EncFor_BomName (int enc, char * bom_name) {
   char p[10];
   int i;

   for (i = 0; *(bom_name + i) != ' '; i++) { fprintf(stderr,"%c",p[i]); p[i] = * (bom_name+i); }
   if ( i>0 ) p[i] = '\0';
  	switch (enc) {
  		case ENC_UTF_8:
  		   if ( strcmp(bom_name,"8") == 0 ) return 1;
  		   break;
  		case ENC_UTF_16LE:
  		   if ( strcmp((char *)p,"16LE") == 0 ) return 1;
  		   break;
  		case ENC_UTF_16BE:
  		   if ( strcmp((char *)p,"16BE") == 0 ) return 1;
  		   break;
  		case ENC_UTF_32LE:
  		   if ( strcmp(bom_name,"32LE") == 0 ) return 1;
  		   break;
  		case ENC_UTF_32BE:
  		   if ( strcmp(bom_name,"32BE") == 0 ) return 1;
  		   break;
  	   default:
		   return 0;
  	};
  	return 0;
}

int r_BOM (char * bom_name, int enc, char who_flag) {
//+  if ( enc == ENC_BOM ) { /* this branch cannot be used from refc!!! */
   if ( (who_flag == RF_REFGO) && (enc == ENC_BOM) ) { /* this branch cannot be used from refc!!! */
      if ( * bom_name == '\0' ) {
      	return -1;
      }
      char * p = "utf-";
      char ch;
      for ( ; *p ; p++ ) { ns2 (* p); };  // In order to overcome the problem, a dummy function named as ns2 is defined in pass2.c 
      for ( ; *bom_name != ' '; bom_name++ ) {
      	ch = tolower(*bom_name);
//      	ns (ch);  // See the remark above.
      	ns2 (ch);
      }
      return 1;
   }

   if ( (* bom_name != '\0') && (enc != ENC_BOM) && (EncFor_BomName(enc, bom_name) != 1)) {
    	fprintf(stderr,"\nWarning: the UTF-%s BOM is considered just as a zero width space,",bom_name);
    	if ( who_flag == RF_REFC ) {
      	fprintf(stderr,"\n refc does not accept the encoding indicated by this BOM.");
  	   } else {
  	   fprintf(stderr," since its byte list contradicts the encoding fixed by a call of the Open built-in function.");
  	   }
      r_bom_warning_st = 1;
   }
  	return 0;
}
/*
# define RF_UTF8_BOM    "8 (0xEFBBBF = 239187191)"
# define RF_UTF16BE_BOM "16BE (0xFEFF = 254255)"
# define RF_UTF16LE_BOM "16LE (0xFFFE = 255254)"

*/

int r_utf_BOM (FILE * fp, int enc) {
    	return r_check_utf_BOM(fp, enc, RF_REFGO);
}

# define enc_is_not(eutf)  (enc != eutf) || (enc == ENC_BOM)

int r_check_utf_BOM (FILE * fp, int enc, char who_flag) {
      int byte_shift = 0;
      unsigned char buf[7];
      long position;
  
      r_bom_warning_st = 0;
                /* Do not remove the following two lines! See Microsoft C documentation on BOM. */
      position = ftell(fp);       
      fseek(fp,position,SEEK_CUR); // The current position of the pointer inside the stream.
      
      for (int i = 0; i < 4; i++ ) {
      	buf[i] = (unsigned char)fgetc(fp);
      };
        // BOM = U+FEFF, see below for its concrete encodings:
      if ( (buf[0] == 0xEF) && (buf[1] == 0xBB) && (buf[2] == 0xBF) )  {   
         if ( enc_is_not(ENC_UTF_8) ) {
         	r_BOM(RF_UTF8_BOM, enc, who_flag);
         } else { byte_shift = 3; } // The byte lenght of the BOM sequence.
      } else if ( (buf[0] == 0xFE) && (buf[1] == 0xFF) ) {
         if ( enc_is_not(ENC_UTF_16BE) ) {
         	r_BOM(RF_UTF16BE_BOM, enc, who_flag);
         } else {	byte_shift = 2; }
      } else if ( (buf[0] == 0xFF) && (buf[1] == 0xFE) ) {
         if ( enc_is_not(ENC_UTF_16LE) ) {
         	r_BOM(RF_UTF16LE_BOM, enc, who_flag);
         } else { byte_shift = 2; }
      } else if ( (buf[0] == 0x00) && (buf[1] == 0x00) && (buf[2] == 0xFE) && (buf[3] == 0xFF) ) {
         if ( enc_is_not(ENC_UTF_32BE) ) {
         	r_BOM(RF_UTF32BE_BOM, enc, who_flag);
         } else {	byte_shift = 4; } // Actually it is still unknown and should be tested.
      } else if ( (buf[0] == 0xFE) && (buf[1] == 0xFF) && (buf[2] == 0x00) && (buf[3] == 0x00) ) {
         if ( enc_is_not(ENC_UTF_32LE) ) {
         	r_BOM(RF_UTF32LE_BOM, enc, who_flag);
         } else {	byte_shift = 4; } 
      } else {
         // No BOM:
         fseek(fp,0,SEEK_SET); // The start position of the pointer inside the stream.
         if (enc == ENC_BOM) return r_BOM(RF_NO_BOM, enc, who_flag);
         return r_BOM(RF_NO_BOM, enc, who_flag);
      }
      if (byte_shift != 0) {
      	fseek(fp,byte_shift,SEEK_SET); // The start position of the pointer inside the stream.
      }
      return r_bom_warning_st;
}

/* Mapping from the Windows code-page numbers to mnemonic names of the code-pages. */
/* This file is used in func1.c */

	     // Nemytykh 12-03-2026

# ifdef BY_GCC_MINGW_WIN 

//-- #include <windows.h>

         // Do not reduce the lenght of the following rests:
static char ri_windows_enc [] = "Windows-__________";
static char ri_utfwin_enc [] = "UTF-_______";
static char ri_koi8win_enc [] = "KOI8-___";
static char ri_cpdos_enc [] = "CP_____";

char * ri_CodePageIdnt (unsigned int UIntCodePage) {
	ri_windows_enc[8] = '\0';
	ri_utfwin_enc[4] = '\0';
	ri_koi8win_enc[5] = '\0';
	ri_cpdos_enc[2] = '\0';
	switch (UIntCodePage) {

     // - Windows code pages (ANSI):
/*
           42 // – Windows Symbol
           874 // – Windows Thai
       // Belarusian be	by	0x0423          1059	 be-BY      be_BY.UTF-8
       // Ukrainian	uk	  uk	0x0422          1058	 uk-UA   ==> uk_UA.UTF-8
*/
	   case 1250:    //  – Windows Central Europe
//		   return "Windows-1250";
         return strcat(ri_windows_enc, "1250");
		   break;
	   case 1251:    //  – Windows Cyrillic  Windows-1251
//		   return = "Windows-1251";
         return strcat(ri_windows_enc, "1251");
		   break;
	   case 1252:    //  – Windows Western
//		   return = "Windows-1252";
         return strcat(ri_windows_enc, "1252");
		   break;
	   case 1253:    //  – Windows Greek
//		   return = "Windows-1253";
         return strcat(ri_windows_enc, "1253");
		   break;
/*
          1254 // – Windows Turkish
          1255 // – Windows Hebrew
          1256 // – Windows Arabic
          1257 // – Windows Baltic
          1258 // – Windows Vietnamese
*/
       // Russian-Moldova	ru	ru-mo	0x0819    2073	 ru_MO
       // Microsoft Unicode code pages:

	   case 1200:  // – UTF-16LE Unicode (little-endian)
//		   return = "UTF-16LE"; 
         return strcat(ri_utfwin_enc, "16LE");
		   break;
	   case 1201:  // – UTF-16BE Unicode (big-endian)
//		   return = "UTF-16BE";
         return strcat(ri_utfwin_enc, "16BE");
		   break;
	   case 12000:  // – UTF-32LE Unicode (little-endian)
//		   return = "UTF-32LE";
         return strcat(ri_utfwin_enc, "32LE");
		   break;
	   case 12001:  // – UTF-32BE Unicode (big-endian)
//		   return = "UTF-32BE";
         return strcat(ri_utfwin_enc, "32BE");
		   break;
             // 65000 // - UTF-7 Unicode
	   case 65001:  // – UTF-8 Unicode       ru_RU.UTF-8 (так в LINUX-е выглядит значение LANG)
//		   return = "UTF-8";
         return strcat(ri_utfwin_enc, "8");
		   break;
             // 65520 – Empty Unicode Plane

     // - KOI8:
	   case 20866:  // 	koi8-r	Русский (KOI8-R);    Кириллица (KOI8-R)
//		   return = "KOI8-R";
		   return strcat(ri_koi8win_enc,"R");
		   break;
	   case 21866:  // 	koi8-u	Украинский (KOI8-U); Кириллица (KOI8-U)
//		   return = "KOI8-U";
		   return strcat(ri_koi8win_enc,"U");
		   break;
            // ????? koi8-b   Болгарский (KOI8-B);    

     // MS-DOS code pages:
             // 708 – Arabic (ASMO 708)
             // 720 – Arabic (Transparent ASMO)
	   case 737:    // – Greek
//		   return = "CP737";
		   return strcat(ri_cpdos_enc,"737");
		   break;
	   case 850:    // – Latin-1
//		   return = "CP850";
		   return strcat(ri_cpdos_enc,"850");
		   break;
	   case 851:    // – Greek
//		   return = "CP851";
		   return strcat(ri_cpdos_enc,"851");
		   break;
	   case 852:    // – Latin-2
//		   return = "CP852";
		   return strcat(ri_cpdos_enc,"852");
		   break;
	   case 855:   // – Cyrillic
//		   return = "CP855";
		   return strcat(ri_cpdos_enc,"855");
		   break;
             // 857 – Latin-5
             // 858 – Latin-1 with euro symbol
             // 859 – Latin-9
             // 860 – Portuguese
             // 861 – Icelandic
             // 862 – Hebrew
             // 863 – Canadian French
             // 864 – Arabic
             // 865 – Danish/Norwegian
	   case 866:   // – Belarusian, Russian, Ukrainian (cp866) -- 866 (DOS)
//		   return = "CP866";
		   return strcat(ri_cpdos_enc,"866");
		   break;
             // 869 – Greek
	   case 878:   // - alias koi8-r for cp878 (code page 878)
//		   return = "CP878";
		   return strcat(ri_cpdos_enc,"878");
		   break;

      default:
		   return "Unknown";
     }
}

char * ri_UserLocaleName(void) {
      wchar_t name[LOCALE_NAME_MAX_LENGTH];
      GetUserDefaultLocaleName(name, LOCALE_NAME_MAX_LENGTH);

   	char utf16mode = ENC_UTF_16LE; 
   	
  		r_sputws_enc_utf16_to_utf8 ((char *)r_cmdarg, (wchar_t *)name, utf16mode);
      char * dist = (char *)r_cmdarg;
      for (int i=0; *(dist+i); i++) {
      	if (*(dist+i) == '-') *(dist+i) = '_';
      }

      return dist;
}

# endif
