/***************************************************************************
 *            reg_deps.c
 *
 *  Mon Apr 18 15:19:04 2005
 *  Copyright  2005  Benjamin Ducke
 ****************************************************************************/

/*
 *  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 Library 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.
 */
 
#include "globals.h"


/*
	Reads in the "depends" file and returns a string of comma-separated
	extensions, on which this one depends.
*/
char *depstr ( char *package, char *gisbase) {
	char file [MAXSTR];
	char line [MAXSTR];
	char *str;
	int first;
	int error;
	FILE *f_deps;
	
	char short_name [MAXSTR];
	
	/* check if 'depends' exists and is readable */
	sprintf (file, "../depends");
	f_deps = fopen ( file, "r" );	
	if ( f_deps == NULL ) {
		if ( errno == ENOENT ) {
			/* file does not exist, return an empty string */
			return ("");
		} else {
			/* sth. strange happened */
			print_error ( ERR_CHECK_DEPS,"checking for file '%s': %s\n", file, strerror (errno)); 
		}
	}		
	
	first = 1;
	str = malloc ( sizeof (char) * MAXSTR );	
	while ( nc_fgets_nb (line, MAXSTR, f_deps) != NULL ) {
		if ( strlen (line) > 0 ) {
			error = sscanf (line, "%s", short_name );
			if ( (error > 0) && ( strcmp ("GRASS",short_name ) ) ) {
				if ( first ) {
					strcat ( str, "\t");
					strcat ( str, short_name );
					first = 0;
				} else {
					strcat ( str, ",");
					strcat ( str, short_name );
				}
			}
		}
	}
	
	fclose ( f_deps );	
	return ( str );	
}

/* 
	Reads in GISBASE/etc/extensions.db 
	Adds a line to the file GISBASE/etc/extensions.db that contains: 
	extension name and version, 'src' or name of binary files.
	Adds extension name to the end of every entry mentioned in 'depends' 
*/
void register_extension ( char *gisbase, char *bins, char *pkg_short_name, 
							int pkg_major, int pkg_minor, int pkg_revision ) {
	char file [MAXSTR];
	char str [MAXSTR];
	int n_lines;
	int error;
	int db_exists;
	int must_register;
	int copy_thru;
	int ext_exists;
	int fd;
	FILE *f_in, *f_out;
	
	char short_name [MAXSTR];
	char inst_bins [MAXSTR];
	char deps [MAXSTR];
	int major, minor, revision;			
	
	
	/* check, if extensions.db exists and is readable */
	db_exists = 1;	
	sprintf (file, "%s/etc/extensions.db", gisbase );
	f_in = fopen ( file, "r" );
	if ( f_in == NULL ) {
		if ( errno == ENOENT ) {
			/* file does not yet exist */
			db_exists = 0;
		} else {
			/* sth. strange happened */
			fclose (f_in);
			print_error ( ERR_REGISTER_EXT,"checking for file '%s': %s\n", file, strerror (errno)); 
		}
	}
	
	if ( db_exists ) {
		/* create a temporary extensions.db file for write access */
		strcpy (TMPDB, "/tmp/grass.extensions.db.XXXXXX"); /* TMPDB is a global variable */
		fd = mkstemp ( TMPDB );
		if ( fd < 0 ) {
			close (fd);
			print_error ( ERR_REGISTER_EXT, "could not create temp file '%s': %s\n \
							Make sure that directory /tmp exists on your system and you have write permission.\n",
							TMPDB, strerror (errno));
		}
				
		f_out = fdopen ( fd, "w+");
		atexit ( &exit_db ); /* now need to register an at exit func to remove tmpdb automatically! */		

		/* count number of extensions.db registry entries */
		n_lines = 0;
		must_register = 1;
		ext_exists = 0;
		while ( nc_fgets_nb (str, MAXSTR, f_in) != NULL ) {
			n_lines ++;
			copy_thru = 1;
			/* read in name and version of registered item */
			sscanf (str, "%s\t%i.%i.%i\t%s\t%s", short_name, &major, &minor, &revision, inst_bins, deps);
			/* check, if extension with same name already exists */
			if ( !strcmp ( short_name, pkg_short_name ) ) {
				/* an extension with this name is already installed */ 				
				ext_exists = 1;
				/* check if it's version is lower, same or higher */
				error = vercmp ( major, minor, revision, pkg_major, pkg_minor, pkg_revision );
				if ( (!FORCE ) ) {
					/* error, if another version is present! */					
					if (  error < 0 ) {
						print_error ( ERR_EXISTS_EXT, "Extension '%s' with lower version (%i.%i.%i) already installed. You can use -f to overwrite this version, if you know what you are doing.\n"
										,pkg_short_name,major, minor, revision);						
					} else {
						print_error ( ERR_EXISTS_EXT, "Extension '%s' with same or higher version (%i.%i.%i) already installed. You can use -f to overwrite this version, if you know what you are doing.\n"
										,pkg_short_name,major, minor, revision);
					}
				}			
				if ( FORCE ) { /* this overrides/implies UPGRADE! */
					if ( error == 0 ) {
						/* if same version installed: just leave everything untouched */
						must_register = 0;
					} else {
						/* otherwise, work needs be done! */
						must_register = 1;
						copy_thru = 0;
					}
				}
			}
			
			/* write line to temporary extension file */
			if ( copy_thru ) { /* just copy thru */
				fprintf ( f_out, str);
				fflush ( f_out );
			}
		}
		
		if ( must_register ) {			
			if ( !ext_exists ) {
				/* register brand new extension */
				sprintf (deps, "%s", depstr ( pkg_short_name, gisbase ));				
				fprintf ( f_out, "%s\t%i.%i.%i\t%s\t%s\n", pkg_short_name, pkg_major, pkg_minor, pkg_revision, bins, deps );
			} else {
				/* register only new version number and binary source, if appropriate */
				sprintf (deps, "%s", depstr ( pkg_short_name, gisbase ));
				fprintf ( f_out, "%s\t%i.%i.%i\t%s\t%s\n", short_name, pkg_major, pkg_minor, pkg_revision, inst_bins, deps );				
			}			
		}					
		
		fclose ( f_out );
		fclose ( f_in );
	}
			
	if ( (n_lines == 0) || (!db_exists) ) {
		/* extensions.db file does not yet exist or is empty: just create a new one */
		strcpy (TMPDB, "/tmp/grass.extensions.db.XXXXXX"); /* tmpdir is a global variable */
		fd = mkstemp ( TMPDB );	
		if ( fd < 0 ) {
			close ( fd );
			print_error ( ERR_REGISTER_EXT, "could not create temp db '%s': %s\n \
					Make sure that directory /tmp exists on your system and you have write permission.\n",
					file, strerror (errno));
		}		
		atexit ( &exit_db ); /* now need to register an at exit func to remove tmpdb automatically! */
		f_out = fdopen ( fd, "w+");
		/* register brand new extension */
		sprintf (deps, "%s", depstr ( pkg_short_name, gisbase ));
		fprintf ( f_out, "%s\t%i.%i.%i\t%s\t%s\n", pkg_short_name, pkg_major, pkg_minor, pkg_revision, bins, deps );
		fclose (f_out);
		close (fd);		
		return;
	}		
}


/*
	Removes an entry from GISBASE/etc/extensions.db
*/
void deregister_extension ( char *package, char *pkg_short_name, char *gisbase ) {
	char file [MAXSTR];
	char str [MAXSTR];
	int error;
	int db_exists;
	int copy_thru;
	int fd;
	FILE *f_in, *f_out;
	int found_ext;
	
	char short_name [MAXSTR];
	char inst_bins [MAXSTR];
	char deps [MAXSTR];
	int major, minor, revision;			
	
	
	db_exists = 0;
	/* check, if extensions.db exists and is readable */
	sprintf (file, "%s/etc/extensions.db", gisbase );
	f_in = fopen ( file, "r" );
	if ( f_in == NULL ) {
		if ( (errno == ENOENT) && (!FORCE) ) {
			/* file does not yet exist */
			fclose (f_in);
			print_error ( ERR_DEREGISTER_EXT, "could not deregister: no extensions installed\n");
		} else {
			/* sth. strange happened */
			if ( !FORCE ) {
				fclose (f_in);
				print_error ( ERR_DEREGISTER_EXT,"checking for file '%s': %s\n", file, strerror (errno)); 
			}
		}
	} else {
		db_exists = 1;
	}
		
	if ( db_exists ) {
		db_exists = 0;
		/* create a temporary extensions.db file for write access */
		strcpy (TMPDB, "/tmp/grass.extensions.db.XXXXXX"); /* tmpdir is a global variable */
		fd = mkstemp ( TMPDB );
		if ( ( fd < 0 ) && ( !FORCE ) ) {
			close ( fd );
			print_error ( ERR_DEREGISTER_EXT, "could not create temp db '%s': %s\n \
							Make sure that directory /tmp exists on your system and you have write permission.\n",
							file, strerror (errno));					
		} else {
			db_exists = 1;
			f_out = fdopen ( fd, "w+");
			atexit ( &exit_db ); /* now need to register an at exit func to remove tmpdb automatically! */							
		}				
	}
	
	/* if creation of temp db file succeeded: */
	if ( db_exists ) {
		/* read every line in extension.db */
		found_ext = 0;
		while ( nc_fgets_nb (str, MAXSTR, f_in) != NULL ) {
			/* copy thru, if extension name different from current extension's */			
			error = sscanf (str, "%s\t%i.%i.%i\t%s\t%s", short_name, &major, &minor, &revision, inst_bins, deps);
			if ( error > 0 ) {
				copy_thru = 1;				
				if ( !strcmp ( pkg_short_name, short_name ) ) {
					copy_thru = 0;
					found_ext = 1;
				}
				if ( copy_thru ) {									
					/* abort, if a dependency on current extension is found */
					if ( (strstr ( deps, pkg_short_name )) && ( !FORCE ) ) {
						print_error ( ERR_DEREGISTER_EXT, "cannot uninstall extension '%s' it is needed by '%s'.\n", 
										pkg_short_name, short_name );
					}					
					fprintf (f_out, str);
				}
			}
		}
		
		/* was the extension installed at all? */
		if ( found_ext == 0 ) {
				print_error ( ERR_DEREGISTER_EXT, "no extension '%s' registered/installed in '%s'.\n",
								pkg_short_name, gisbase);
		}
		/* mind the right sequence for closing these! */
		fclose ( f_in );
		fclose ( f_out );
		close ( fd );			
	}				
}


/*
	checks GRASS version number and whether all extensions listed in 'depends'
	are satisfied (by analysing GISBASE/etc/extensions.db).
	Terminates program, if missing deps; just returns, if "--force" was given.
*/
void check_dependencies ( char *package, char *gisbase, char *grass_version ) {
	char file [MAXSTR];
	char str [MAXSTR];
	char dbstr [MAXSTR];
	int error;
	int db_exists;
	int satisfied;
	FILE *f_deps, *f_db;
	
	char short_name [MAXSTR];	
	int major, minor, revision;
	char dep_short_name [MAXSTR];
	int dep_major, dep_minor, dep_revision;
	
	if ( FORCE ) {
		return;
	}
	
	/* check if 'depends' exists and is readable */	
	f_deps = fopen ( "../depends", "r" );	
	if ( f_deps == NULL ) {
		if ( errno == ENOENT ) {
			/* file does not exist, no need to check for deps */
			fprintf (stderr, "\n%s/depends ENOENT\n", package);
			return;
		} else {
			/* sth. strange happened */
			fclose (f_deps);
			print_error ( ERR_CHECK_DEPS, "checking for file '%s': %s\n", file, strerror (errno));
		}
	}	
	
	db_exists = 1;
	/* check if extensions.db exists and is readable */
	sprintf (file, "%s/etc/extensions.db", gisbase );
	f_db = fopen ( file, "r" );
	if ( f_db == NULL ) {
		if ( errno == ENOENT ) {
			/* file does not exist */
			db_exists = 0;
		} else {
			/* sth. strange happened */
			fclose (f_db);
			print_error ( ERR_CHECK_DEPS, "checking for file '%s': %s\n", file, strerror (errno));
		}
	}
		
	satisfied = 0;			
	while ( nc_fgets_nb (str, MAXSTR, f_deps) != NULL ) {
		/* get first line with dependencies info */
		major = 0;
		minor = 0;
		revision = 0;
		dep_major = 0;
		dep_minor = 0;
		dep_revision = 0;		
		error = sscanf (str, "%s\t%i.%i.%i", dep_short_name, &dep_major, &dep_minor, &dep_revision);
		if ( error > 0 ) {
			if ( !strcmp (dep_short_name, "GRASS") ) {
				/* a GRASS version dependency has been specified */				
				sscanf ( grass_version, "%i.%i.%i", &major, &minor, &revision );
				if ( vercmp ( major, minor, revision, dep_major, dep_minor, dep_revision ) < 0 ) {
					print_error ( ERR_MISSING_DEPS,"installed version (%s) of GRASS is too low. Required version is %i.%i.%i\n",
								grass_version, dep_major, dep_minor, dep_revision);
				}
				satisfied = 1; /* let's be tolerant */
			} else {				
				/* a dependency on some other extension has been specified ... */
				if ( db_exists ) { /* ... if no extensions installed yet, it must be unsatisfied! */
					satisfied = 0;
					rewind ( f_db );
					while ( nc_fgets_nb ( dbstr, MAXSTR, f_db ) != NULL ) {
						major = 0;
						minor = 0;
						revision = 0;
						error = sscanf ( dbstr, "%s\t%i.%i.%i", short_name, &major, &minor, &revision );
						if ( error > 0 ) {
							if ( !strcmp ( short_name, dep_short_name ) ) {
								/* found an installed extension that satisfies name, now check
								   for version number */
								if ( vercmp  (major, minor, revision, dep_major, dep_minor, dep_revision ) < 0 ) {
									print_error ( ERR_MISSING_DEPS, "installed version %i.%i.%i of required extension '%s' is too low.\n \
													Required version is %i.%i.%i\n",
													major, minor, revision, dep_short_name,
													dep_major, dep_minor, dep_revision ); 
								}
								/* name and version dependencies satisfied */
								satisfied = 1;
							}
						}
					}
					if ( !satisfied ) {
					 	print_error ( ERR_MISSING_DEPS, "required extension '%s' not found in '%s'.\n", dep_short_name, gisbase );
					}
				}
			}
		}
	}
	if ( db_exists ) {
		fclose ( f_db );
	}
	fclose ( f_deps );

}
