/*****************************************************************************

GRAOUMF TRACKER 2

Copyright (c) 1996 - 2002 Laurent de Soras

This program 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
of the License, or (at your option) any later version.

This program 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 this program; if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.

Contact the author : laurent@ohmforce.com
More information about this license : http://www.gnu.org/licenses/gpl.html

*****************************************************************************/



/*\\\ FICHIERS INCLUDE \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/

#include	<stdio.h>
#include	<time.h>
#include	<stdlib.h>
#include	<assert.h>

#include	<direct.h>
#include	<io.h>
#define	WIN32_LEAN_AND_MEAN
#include <windows.h>
#include <windowsx.h>

#include	"archi.h"
#include	"base.h"
#include	"ConfigFile.h"
#include	"ConfigKey.h"
#include	"ConfigKeyValue.h"
#include	"file.h"
#include	"file_ct.h"
#include	"fnames.h"
#include	"gtracker.h"
#include	"intrface.h"
#include	"keyboard.h"
#include	"List.h"
#include	"log.h"
#include	"player.h"
#include	"String.h"



/*\\\ CONSTANTES PRIVEES \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/



/*\\\ TYPES & STRUCTURES PRIVES \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/



/*\\\ PROTOTYPES PRIVES \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/



/*\\\ VARIABLES EXTERNES \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/

/* C'est le caractere de separation de chemin */
const char	FILE_path_separator_0 [] = "\\";

/* Noms de fichiers complets (path, name, ext) */
/* Le nom du fichier (name + ext) peut etre vide selon l'humeur. */
String	FILE_module_filename;
String	FILE_sample_filename;
String	FILE_instrument_filename;
String	FILE_prefs_filename;
String	FILE_auto_module_filename;
String	FILE_misc_filename;

/* Noms de chemin (path) */
String	FILE_program_pathname;
String	FILE_auto_sample_pathname;

/* Masques avec jockers (names, ext) */
String	FILE_module_maskname;
String	FILE_song_maskname;
String	FILE_sample_maskname;
String	FILE_instrument_maskname;
String	FILE_prefs_maskname;
String	FILE_auto_sample_maskname;



/*\\\ VARIABLES PRIVEES \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/

/* Liste de fichier dans un repertoire */
struct _finddata_t	FILE_disk_buffer;	/* Buffer pour la liste d'une directory */
intptr_t	FILE_directory_handle = -1;	/* Handle utilise lors de la liste d'une directory (-1 = aucun actuellement) */

OSVERSIONINFO	FTLE_windows_version;	/* Informations sur la version de Windows */
HINSTANCE	FILE_kernel32_dll_handle;	/* Handle de KERNEL32.DLL */

/* Adresse de la fonction de recherche d'espace disque pour Win NT ou OSR 2 */
typedef	bool (__stdcall *FILE_GetDiskFreeSpaceExPtrType) (
	LPCTSTR lpDirectoryName,
	PULARGE_INTEGER lpFreeBytesAvailableToCaller,
	PULARGE_INTEGER lpTotalNumberOfBytes,
	PULARGE_INTEGER lpTotalNumberOfFreeBytes
);
FILE_GetDiskFreeSpaceExPtrType	FILE_GetDiskFreeSpaceEx_ptr = NULL;



/*\\\ FONCTIONS \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\*/



/*==========================================================================*/
/*      Nom: FILE_read_string_in_file                                       */
/*      Description: Lit une chaine entouree de guillemets "" dans un       */
/*                   fichier, et precedee de sa longueur maximum.           */
/*      Parametres en entree:                                               */
/*        - buffer_0: buffer servant de reception a la chaine (0 final).    */
/*                    L'espace doit etre prevu suffisemment grand.          */
/*        - file_ptr: pointeur sur les informations du fichier.             */
/*      Retour: la longueur maximum de la chaine.                           */
/*==========================================================================*/

int	FILE_read_string_in_file (char *buffer_0, FILE *file_ptr)
{
	assert (buffer_0 != 0);
	assert (file_ptr != 0);

	int				string_len = 0;
	const int		ret_val = fscanf (file_ptr, "%d ", &string_len);
	if (ret_val != 1)
	{
		LOG_printf ("FILE_read_string_in_file: Cannot read string len.");
		assert (false);
	}

	int				char_cpt = -1;
	while (char_cpt < string_len)
	{
		char				c = fgetc (file_ptr);
		if (char_cpt == -1)
		{
			if (c == '\"')
			{
				char_cpt = 0;
			}
		}
		else
		{
			buffer_0 [char_cpt] = c;
			char_cpt ++;
		}
	}
	fgetc (file_ptr);	/* Saute la deuxieme apostrophe */
	buffer_0 [char_cpt] = '\0';

	return (string_len);
}



/****************************************************************************/
/*                                                                          */
/*      INITIALISATIONS                                                     */
/*                                                                          */
/****************************************************************************/



/*==========================================================================*/
/*      Nom: FILE_get_program_path                                          */
/*      Description: Recupere le chemin du programme. Cette fonction doit   */
/*                   etre appelee avant toute fonction de sortie du fichier */
/*                   de log.                                                */
/*      Retour: 0 si tout va bien.                                          */
/*==========================================================================*/

signed int FILE_get_program_path (void)
{
	char				pathname_0 [FNAM_PATHNAME_MAXLEN+1];
	char *			temp_0 [3];

/*______________________________________________
 *
 * Recupere le chemin de lancement
 *______________________________________________
 */

	strncpy (pathname_0, GetCommandLine (), FNAM_PATHNAME_MAXLEN);
	pathname_0 [FNAM_PATHNAME_MAXLEN] = '\0';
	BASE_parse_command_line (pathname_0, temp_0, 2, NULL);
	FILE_program_pathname = FNAM_get_full_path (temp_0 [0]);
	strcpy (LOG_pathname_0, (FILE_program_pathname + LOG_FILENAME_0).c_str ());

/* Cette fonction retourne en fait le repertoire depuis lequel
	le programme a ete execute. Ce n'est pas ce que l'on veut. */
#if 0
	long		antislash;

	if (_getdcwd (0, pathname_0, FNAM_PATHNAME_MAXLEN) == NULL)
	{
		LOG_printf ("FILE_init_paths: Error: couldn't get working directoroy.\n");
		return (-1);
	}
	FILE_program_pathname = String (pathname_0).trim ();
	antislash = FILE_program_pathname.rfind (FILE_path_separator_0 [0]);
	if (antislash != FILE_program_pathname.get_len () - 1)
	{
		FILE_program_pathname += FILE_path_separator_0;
	}

	LOG_printf ("Main directory: %s\n", FILE_program_pathname);

#endif

	return (0);
}



/*==========================================================================*/
/*      Nom: FILE_init                                                      */
/*      Description: Initialise les differents chemins.                     */
/*      Retour: 0 si tout va bien.                                          */
/*==========================================================================*/

signed int FILE_init (void)
{
/*______________________________________________
 *
 * Noms de fichiers (complets)
 *______________________________________________
 */

	/* Module */
	FILE_generate_new_module_name ();

	/* Sample */
	FILE_sample_filename = FILE_program_pathname + "NoName00.wav";

	/* Instruments */
	FILE_instrument_filename = FILE_program_pathname + "NoName00.ins";

	/* Prefs */
	FILE_prefs_filename = FILE_program_pathname + "pref.inf";

	/* Autoloading module */
	FILE_auto_module_filename = FILE_program_pathname;

	/* Miscellaneous */
	FILE_misc_filename = FILE_program_pathname;

/*______________________________________________
 *
 * Noms de chemins
 *______________________________________________
 */

	/* Autoloading sample */
	FILE_auto_sample_pathname = FILE_program_pathname;

/*______________________________________________
 *
 * Masques
 *______________________________________________
 */

	FILE_module_maskname = "*.GT2";
	FILE_song_maskname = "*.SNG";
	FILE_sample_maskname = "*.WAV";
	FILE_instrument_maskname = "*.INS";
	FILE_prefs_maskname = "*.INF";
	FILE_auto_sample_maskname = "*.WAV";

/*______________________________________________
 *
 * Recuperation de la fonction de recherche de
 * l'espace libre sur le disque
 *______________________________________________
 */

	/* Quelle est la version de Vindoze sur laquelle on travaille ? */
	FTLE_windows_version.dwOSVersionInfoSize = sizeof (FTLE_windows_version);
	if (! GetVersionEx (&FTLE_windows_version))
	{
		LOG_printf ("FILE_init: Error: couldn't get Windows version.\n");
		return (-1);
	}

	/* Chargement de la fonction de recherche de l'espace disque restant */
	if (   FTLE_windows_version.dwPlatformId == VER_PLATFORM_WIN32_NT
	    || (   FTLE_windows_version.dwPlatformId == VER_PLATFORM_WIN32_WINDOWS
	        && (FTLE_windows_version.dwBuildNumber & 0xFFFF) > 1000))
	{
		FILE_kernel32_dll_handle = LoadLibrary ("KERNEL32.DLL");
		if (FILE_kernel32_dll_handle == NULL)
		{
			LOG_printf ("FILE_init: Warning: couldn't load KERNEL32.DLL.\n");
		}

		FILE_GetDiskFreeSpaceEx_ptr =
			(FILE_GetDiskFreeSpaceExPtrType) GetProcAddress (FILE_kernel32_dll_handle, "GetDiskFreeSpaceExA");
		if (FILE_GetDiskFreeSpaceEx_ptr == NULL)
		{
			LOG_printf ("FILE_init: Warning: couldn't get GetDiskFreeSpaceExA routine address.\n");
		}
	}

	return (0);
}



void	FILE_restore (void)
{
}



/*==========================================================================*/
/*      Nom: FILE_generate_new_module_name                                  */
/*      Description: Genere un nom de module. Fonction utilisee lors de     */
/*                   la creation d'un nouveau module "from scratch".        */
/*==========================================================================*/

void	FILE_generate_new_module_name (void)
{
	static int	mod_count = 0;
	char		filename_0 [31+1];

	sprintf (filename_0, "NoName%02d.gt2", mod_count);
	FILE_module_filename = FILE_program_pathname + filename_0;
	mod_count ++;
}



/*==========================================================================*/
/*      Nom: FILE_load_init_prefs                                           */
/*      Description: Charge le fichier des preferences                      */
/*      Retour: 0 si aucune erreur.                                         */
/*==========================================================================*/

signed int	FILE_load_init_prefs (void)
{
	signed int	ret_val;

	/* Si le fichier de prefs n'existe pas, on se barre. */
	if (! FILE_file_exist (FILE_prefs_filename.c_str ()))
	{
		return (0);
	}

	LOG_printf ("Loading prefs:\n");

	ConfigFile	cfg_file (FILE_prefs_filename.c_str ());
	std::list <ConfigKey>	key_list;
	if (cfg_file.read (key_list))
	{
		LOG_printf ("FILE_load_init_prefs: Error: couldn't load preference file.\n");
	}

	for (std::list <ConfigKey>::const_iterator it = key_list.begin ()
	;	it != key_list.end ()
	;	++ it)
	{
		/* Recupere la cle */
		const ConfigKey &	key = *it;

		/* Recupere son nom */
		const String	key_name = key.get_name ();
		const ConfigKeyValue &	key_val = key.get_value ();
		const String	key_str = key_val.get_string ();

		/* Cle anonyme: interdit */
		if (BASE_compare_string (key_name.c_str (), "") == 0)
		{
			LOG_printf ("INTR_read_display_conf: Warning: Unexpected unassigned value.\n");
		}

		/* Keyboard */
		else if (BASE_compare_string (key_name.c_str (), "keyboard") == 0)
		{
			if (key_val.get_type () != ConfigKeyValue_Type_LIST)
			{
				LOG_printf ("FILE_load_init_prefs: Warning: (in \"keyboard\") argument must be a list.\n");
			}
			else
			{
				ret_val = KEYB_read_config_list (key_val.get_list ());
			}
		}

		/* Display */
		else if (BASE_compare_string (key_name.c_str (), "display") == 0)
		{
			if (key_val.get_type () != ConfigKeyValue_Type_LIST)
			{
				LOG_printf ("FILE_load_init_prefs: Warning: (in \"display\") argument must be a list.\n");
			}
			else
			{
				ret_val = INTR_read_display_conf (key_val.get_list ());
			}
		}

		/* Module editing */
		else if (BASE_compare_string (key_name.c_str (), "module_editing") == 0)
		{
			if (key_val.get_type () != ConfigKeyValue_Type_LIST)
			{
				LOG_printf ("FILE_load_init_prefs: Warning: (in \"module_editing\") argument must be a list.\n");
			}
			else
			{
				ret_val = GTK_read_module_editing_conf (key_val.get_list ());
			}
		}

		/* Player */
		else if (BASE_compare_string (key_name.c_str (), "player") == 0)
		{
			if (key_val.get_type () != ConfigKeyValue_Type_LIST)
			{
				LOG_printf ("FILE_load_init_prefs: Warning: (in \"player\") argument must be a list.\n");
			}
			else
			{
				Player &			player = Player::use_instance ();
				ret_val = player.read_conf (key_val.get_list ());
			}
		}

		/* Cle inconnue */
		else
		{
			LOG_printf ("FILE_load_init_prefs: Warning: syntax error \"%s\".\n",
			            key_name.c_str ());
		}

		if (ret_val)
		{
			break;
		}
	}

	if (ret_val)
	{
		return (-1);
	}

	LOG_printf ("\tOK.\n");

	return (0);
}



/*==========================================================================*/
/*      Nom: FILE_autoloading                                               */
/*      Description: Autochargement des samples, modules, etc...            */
/*      Retour: 0 si tout s'est bien passe.                                 */
/*==========================================================================*/

signed int	FILE_autoloading (void)
{
/*
	LOG_printf ("Autoloading:\n");
*/

	/*** A finir ***/

/*
	LOG_printf ("\tOK.\n");
*/

	return (0);
}



/****************************************************************************/
/*                                                                          */
/*      DISK                                                                */
/*                                                                          */
/****************************************************************************/



/*==========================================================================*/
/*      Nom: FILE_drive_connected                                           */
/*      Description: Donne l'activite d'un lecteur                          */
/*      Parametres en entree:                                               */
/*        - drive: numero du drive interroge (A=0, B=1, ...)                */
/*      Retour: true si le lecteur est actif, false sinon.                  */
/*==========================================================================*/

bool	FILE_drive_connected (int drive)
{
	assert (drive >= 0);

	const LWORD		drive_map = GetLogicalDrives ();
	const bool		drive_flag = ((drive_map & (1 << drive)) != 0);

	return (drive_flag);
}



/*==========================================================================*/
/*      Nom: FILE_get_current_drive                                         */
/*      Description: Renvoie le numero du lecteur courant: A=0, B=1,...     */
/*      Retour: Numero du lecteur.                                          */
/*==========================================================================*/

int	FILE_get_current_drive (void)
{
	return (_getdrive () - 1);
}



/*==========================================================================*/
/*      Nom: FILE_get_first_file                                            */
/*      Description: Recopie le bloc d'informations du premier fichier ou   */
/*                   dossier d'un repertoire.                               */
/*      Parametres en entree:                                               */
/*        - path_0: pointe sur le chemin complet du repertoire, suivi du    */
/*                  filtre de noms. Se termine par 0.                       */
/*      Parametres en sortie:                                               */
/*        - ptr_dir_entry: pointeur sur la structure d'informations du      */
/*                         fichier.                                         */
/*        - filename_0: pointeur sur un espace reserve pour mettre le nom   */
/*                      du fichier. La place prevue doit etre suffisante.   */
/*      Retour: true si un fichier a ete trouve, false sinon ou si erreur.  */
/*==========================================================================*/

bool	FILE_get_first_file (const char *path_0, FILE_DIR_ENTRY *ptr_dir_entry, char *filename_0)
{
	assert (path_0 != 0);
	assert (ptr_dir_entry != 0);
	assert (filename_0 != 0);

	if (FILE_directory_handle != -1)
	{
		_findclose (FILE_directory_handle);
		FILE_directory_handle = -1;
	}

	FILE_directory_handle = _findfirst (path_0, &FILE_disk_buffer);
	if (FILE_directory_handle == -1)
	{
		return (false);
	}

	strcpy (filename_0, FILE_disk_buffer.name);
	ptr_dir_entry->name_0 = filename_0;
	ptr_dir_entry->attrib = FILE_disk_buffer.attrib;
	ptr_dir_entry->length = FILE_disk_buffer.size;
	ptr_dir_entry->selected = false;

	struct tm *		tm_ptr = localtime (&FILE_disk_buffer.time_write);
	if (tm_ptr != NULL)
	{
		ptr_dir_entry->minute = (BYTE) tm_ptr->tm_min;
		ptr_dir_entry->hour = (BYTE) tm_ptr->tm_hour;
		ptr_dir_entry->day = (BYTE) tm_ptr->tm_mday;
		ptr_dir_entry->month = (BYTE) tm_ptr->tm_mon + 1;
		ptr_dir_entry->year = (WORD) (tm_ptr->tm_year + 1900);
	}
	else
	{
		ptr_dir_entry->minute = (BYTE) 0;
		ptr_dir_entry->hour = (BYTE) 0;
		ptr_dir_entry->day = (BYTE) 0;
		ptr_dir_entry->month = (BYTE) 0;
		ptr_dir_entry->year = (WORD) 0;
	}

	return (true);
}



/*==========================================================================*/
/*      Nom: FILE_get_next_file                                             */
/*      Description: Recopie le bloc d'informations du prochain fichier ou  */
/*                   dossier d'un repertoire.                               */
/*      Parametres en sortie:                                               */
/*        - ptr_dir_entry: pointeur sur la structure d'informations du      */
/*                         fichier.                                         */
/*        - filename_0: pointeur sur un espace reserve pour mettre le nom   */
/*                      du fichier. La place prevue doit etre suffisante.   */
/*      Retour: true si un fichier a ete trouve, false sinon ou si erreur.  */
/*==========================================================================*/

bool	FILE_get_next_file (FILE_DIR_ENTRY *ptr_dir_entry, char *filename_0)
{
	assert (ptr_dir_entry != 0);
	assert (filename_0 != 0);

	int				result = _findnext (FILE_directory_handle, &FILE_disk_buffer);
	if (result != 0)
	{
		_findclose (FILE_directory_handle);
		FILE_directory_handle = -1;
		return (false);
	}

	strcpy (filename_0, FILE_disk_buffer.name);
	ptr_dir_entry->name_0 = filename_0;
	ptr_dir_entry->attrib = FILE_disk_buffer.attrib;
	ptr_dir_entry->length = FILE_disk_buffer.size;
	ptr_dir_entry->selected = false;

	struct tm *		tm_ptr = localtime (&FILE_disk_buffer.time_write);
	if (tm_ptr != NULL)
	{
		ptr_dir_entry->minute = (BYTE) tm_ptr->tm_min;
		ptr_dir_entry->hour = (BYTE) tm_ptr->tm_hour;
		ptr_dir_entry->day = (BYTE) tm_ptr->tm_mday;
		ptr_dir_entry->month = (BYTE) tm_ptr->tm_mon + 1;
		ptr_dir_entry->year = (WORD) (tm_ptr->tm_year + 1900);
	}
	else
	{
		ptr_dir_entry->minute = (BYTE) 0;
		ptr_dir_entry->hour = (BYTE) 0;
		ptr_dir_entry->day = (BYTE) 0;
		ptr_dir_entry->month = (BYTE) 0;
		ptr_dir_entry->year = (WORD) 0;
	}

	return (true);
}



/*==========================================================================*/
/*      Nom: FILE_get_free_disk_space                                       */
/*      Description: Donne la memoire libre sur le disque.                  */
/*      Parametres en entree:                                               */
/*        - drive: numero du lecteur a inspecter (0-...)                    */
/*      Parametres en sortie:                                               */
/*        - free: pointeur sur la memoire libre du disque                   */
/*        - total: pointeur sur la memoire totale du disque                 */
/*      Retour: 0 si tout s'est bien passe                                  */
/*==========================================================================*/

signed int	FILE_get_free_disk_space (int drive, double *free, double *total)
{
	assert (drive >= 0);
	assert (free != 0);
	assert (total != 0);

	char		dir_0 [3+1];
	ULWORD	cluster_size;
	DWORD		sector_per_cluster;
	DWORD		bytes_per_sector;
	DWORD		avail_clusters;
	DWORD		total_clusters;
	ULARGE_INTEGER	bytes_free;
	ULARGE_INTEGER	bytes_total;

	*free = 0;
	*total = 1;

	sprintf (dir_0, "%c:\\", (char) (drive + 'A'));

	/* NT, Win95 OSR2, Win98 et sup */
	if (FILE_GetDiskFreeSpaceEx_ptr != NULL)
	{
		if (FILE_GetDiskFreeSpaceEx_ptr (dir_0, &bytes_free, &bytes_total, NULL) == 0)
		{
			LOG_printf ("FILE_get_free_disk_space: Error: couldn't get free disk space on drive %s using GetDiskFreeSpaceEx routine.\n", dir_0);
			return (-1);
		}

		*free = bytes_free.HighPart * 4294967296.0 + bytes_free.LowPart;
		*total = bytes_total.HighPart * 4294967296.0 + bytes_total.LowPart;
	}

	/* Win95 de base */
	else
	{
		if (GetDiskFreeSpace (dir_0,
									 &sector_per_cluster,
									 &bytes_per_sector,
									 &avail_clusters,
									 &total_clusters) == 0)
		{
			LOG_printf ("FILE_get_free_disk_space: Error: couldn't get free disk space on drive %s using GetDiskFreeSpace routine.\n", dir_0);
			return (-1);
		}
		cluster_size = sector_per_cluster * bytes_per_sector;
		*free = (double)avail_clusters * cluster_size;
		*total = (double)total_clusters * cluster_size;
	}

	return (0);
}



/*==========================================================================*/
/*      Nom: FILE_get_file_length                                           */
/*      Description: Donne la longueur d'un fichier.                        */
/*      Parametres en entree/sortie:                                        */
/*        - file_ptr: pointeur de fichier. Son contenu peut etre modifie    */
/*                    compte tenu des differentes implementations de la     */
/*                    routine.                                              */
/*      Retour: La longueur du fichier.                                     */
/*==========================================================================*/

long	FILE_get_file_length (FILE *file_ptr)
{
	assert (file_ptr != 0);

	int				handle = _fileno (file_ptr);

	return ((long) _filelength (handle));
}



long	FILE_get_file_length (const char *filename_0)
{
	assert (filename_0 != 0);
	assert (filename_0 [0] != '\0');

	FILE *			file_ptr = fopen (filename_0, "rb");
	if (file_ptr == NULL)
	{
		LOG_printf ("FILE_get_file_length: Error: couldn't open file %s.\n",
		            filename_0);
		return (-1);
	}

	const long		length = FILE_get_file_length (file_ptr);

	fclose (file_ptr);

	return (length);
}



/*==========================================================================*/
/*      Nom: FILE_change_file_length                                        */
/*      Description: Change la longueur d'un fichier.                       */
/*      Parametres en entree:                                               */
/*        - length: Nouvelle longueur du fichier                            */
/*      Parametres en entree/sortie:                                        */
/*        - file_ptr: pointeur de fichier. Son contenu peut etre modifie    */
/*                    compte tenu des differentes implementations de la     */
/*                    routine.                                              */
/*      Retour: 0 si tout s'est bien passe.                                 */
/*==========================================================================*/

signed int	FILE_change_file_length (FILE *file_ptr, long length)
{
	assert (file_ptr != 0);
	assert (length >= 0);

	const int		handle = _fileno (file_ptr);

	return (_chsize (handle, length));
}



/*==========================================================================*/
/*      Nom: FILE_file_exist                                                */
/*      Description: Cherche si un fichier existe deja.                     */
/*      Parametres en entree:                                               */
/*        - filename_0: nom du fichier (avec ou sans chemin) a tester       */
/*      Retour: true si le fichier a ete trouve, false sinon ou si erreur.  */
/*==========================================================================*/

bool	FILE_file_exist (const char *filename_0)
{
	assert (filename_0 != 0);
	assert (filename_0 [0] != '\0');

	if (_access (filename_0, 0) == 0)
	{
		return (true);
	}

	return (false);
}



/*==========================================================================*/
/*      Nom: FILE_back_up                                                   */
/*      Description: Cree un backup d'un fichier. Les backups multiples     */
/*                   sont supportes. Les backup sont appeles .bak, .bak2,   */
/*                   .bak3, etc.                                            */
/*      Parametres en entree:                                               */
/*        - filename_0: chemin complet du fichier a backuper.               */
/*        - nbr_backup: nombre de backups a gerer (1 = normal, 0 = pas de   */
/*                      backup, 2 a 9 = backups multiples.                  */
/*      Retour: 0 si tout s'est bien passe.                                 */
/*==========================================================================*/

signed int	FILE_backup (const char *filename_0, int nbr_backups)
{
	assert (filename_0 != 0);
	assert (filename_0 [0] != '\0');
	assert (nbr_backups >= 0);

	char		nbr3_0 [3+1];
	String	filename;
	String	older_filename;

	/* Trois chiffres max */
	nbr_backups = MIN (nbr_backups, 999);

	if (nbr_backups <= 0)
	{
		return (0);
	}

	/* Detruit le tout dernier backup */
	older_filename = String (filename_0) + ".bak";
	if (nbr_backups > 1)
	{
		sprintf (nbr3_0, "%d", nbr_backups);
		older_filename += nbr3_0;
	}
	if (FILE_file_exist (older_filename.c_str ()))
	{
		if (remove (older_filename.c_str ()) != 0)
		{
			LOG_printf ("FILE_back_up: Error: couldn't delete oldest backup of file %s.\n",
			            filename_0);
			return (-1);
		}
	}

	/* Renomme tous les anciens backups */
	for (int cnt = nbr_backups - 1; cnt >= 0; -- cnt)
	{
		filename = filename_0;
		if (cnt > 0)
		{
			filename += ".bak";
			if (cnt > 1)
			{
				sprintf (nbr3_0, "%d", cnt);
				filename += nbr3_0;
			}
		}

		if (FILE_file_exist (filename.c_str ()))
		{
			if (rename (filename.c_str (), older_filename.c_str ()) != 0)
			{
				LOG_printf ("FILE_back_up: Error: couldn't rename file %s into %s.\n",
				            filename.c_str (), older_filename.c_str ());
				return (-1);
			}
		}
	
		older_filename = filename;
	}

	return (0);
}



/*==========================================================================*/
/*      Nom:                                                                */
/*      Description:                                                        */
/*      Parametres en entree:                                               */
/*      Parametres en sortie:                                               */
/*      Parametres en entree/sortie:                                        */
/*      Retour:                                                             */
/*==========================================================================*/
