<commit_before>/*
 * Configuration-File Functions.
 *
 *  Copyright 1993, 1994, 1997 The Free Software Foundation
 *
 * Authors: <NAME>

   This program is free software; you can redistribute it and/or
   modify it under the terms of the GNU Library General Public License
   as published by the Free Software Foundation; either version 2 of
   the License, or (at your option) any later version.
   
   The Gnome Library 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 Library General Public
   License along with the Gnome Library; see the file COPYING.LIB.  If not,
   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
   Boston, MA 02111-1307, USA.  */

#include <config.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>	/* unlink() */
#include <stdlib.h>	/* atoi() */
#include <sys/types.h>
#include <sys/stat.h>
#include <glib.h>
#include "libgnomeP.h"

#ifdef HAVE_STRNDUP
#include <string.h>
#else
/* Like strdup, but only copy N chars.  */
extern char *strndup (const char *s, size_t n);
#endif

#define STRSIZE 4096
#define overflow (next == &CharBuffer [STRSIZE-1])

enum {
	FirstBrace,
	OnSecHeader,
	IgnoreToEOL,
	KeyDef,
	KeyDefOnKey,
	KeyValue
};

typedef struct {
	int type;
	void *value;
} iterator_type;

typedef enum {
	LOOKUP,
	SET
} access_type;

typedef struct TKeys {
	char *key_name;
	char *value;
	struct TKeys *link;
} TKeys;

typedef struct TSecHeader {
	char *section_name;
	TKeys *keys;
	struct TSecHeader *link;
} TSecHeader;

typedef struct TProfile {
	char *filename;
	time_t mtime;
	int written_to;
	int to_be_deleted;
	TSecHeader *section;
	struct TProfile *link;
} TProfile;

/*
 * Prefix for all the configuration operations
 * iff the path does not begin with / or with #
 */

#define prefix (prefix_list ? prefix_list->data : NULL)

static GSList *prefix_list = NULL;

static TProfile *Current = 0;

/*
 * This one keeps track of all of the opened files
 */
static TProfile *Base = 0;

/*
 * a set handler is a function which is called every time a gnome_config_set_*
 * function is called, this can be used by the app to say guarantee a sync,
 * apps using libgnomeui should not call this, libgnomeui already provides
 * this, this would be for non-gui apps and apps that use a different toolkit
 * than gtk.
 */
static void (*set_handler)(void *data) = NULL;
static void *set_handler_data;

#define CALL_SET_HANDLER() { if(set_handler) (*set_handler)(set_handler_data); }

/* same as above for a "sync" handler */
static void (*sync_handler)(void *data) = NULL;
static void *sync_handler_data;

#define CALL_SYNC_HANDLER() { if(sync_handler) (*sync_handler)(sync_handler_data); }



/* The `release_path' and `parsed_path' routines are inside the
   following file.  It is in a separate file to allow the test-suite
   to get at it, without needing to export special symbols.

   typedef struct {
	char *file, *section, *key, *def;
	char *path, *opath;
   } ParsedPath;

   static void release_path (ParsedPath *p);
   static ParsedPath *parse_path (const char *path, gint priv); */
#include "parse-path.cP"

static int 
is_loaded (const char *filename, TSecHeader **section)
{
	TProfile *p = Base;
	TProfile *lastp = NULL;
	struct stat st;
	
	/*if the last one we accessed was this one we don't want to
	  search*/
	if(Current && strcasecmp (filename, Current->filename) == 0){
		if (stat (filename, &st) == -1)
			st.st_mtime = 0;
		if (Current->mtime != st.st_mtime)
			return 0;
		*section = Current->section;
		return 1;
	}
	
	while (p){
		/*search and destroy empty nodes*/
		if (p->filename[0]=='\0') {
			TProfile *next = p->link;
			if(lastp)
				lastp->link = next;
			else /*the next one is the first one actually*/
				Base = next;
			g_free(p->filename);
			g_free(p);
			p = next;
		} else if (strcasecmp (filename, p->filename) == 0){
			if (stat (filename, &st) == -1)
				st.st_mtime = 0;
			if (p->mtime != st.st_mtime)
				return 0;
			Current = p;
			*section = p->section;
			return 1;
		} else {
			lastp = p;
			p = p->link;
		}
	}
	return 0;
}

static char *
decode_string_and_dup (char *s)
{
	char *p = g_malloc (strlen (s) + 1);
	char *q = p;

	do {
		if (*s == '\\'){
			switch (*(++s)){
			case 'n':
				*p++ = '\n';
				break;
			case '\\':
				*p++ = '\\';
				break;
			case 'r':
				*p++ = '\r';
				break;
			default:
				*p++ = '\\';
				*p++ = *s;
			}
		} else
			*p++ = *s;
	} while (*s++);
	return q;
}

static char *
escape_string_and_dup (char *s)
{
	char *return_value, *p = s;
	int len = 0;

	if(!s)
		return g_strdup("");
	
	while (*p){
		len++;
		if (*p == '\n' || *p == '\\' || *p == '\r' || *p == '\0')
			len++;
		p++;
	}
	return_value = p = (char *) g_malloc (len + 1);
	if (!return_value)
		return 0;
	do {
		switch (*s){
		case '\n':
			*p++ = '\\';
			*p++ = 'n';
			break;
		case '\r':
			*p++ = '\\';
			*p++ = 'r';
			break;
		case '\\':
			*p++ = '\\';
			*p++ = '\\';
			break;
		default:
			*p++ = *s;
		}
	} while (*s++);
	return return_value;
}

static TSecHeader *
load (const char *file)
{
	FILE *f;
	int state;
	TSecHeader *SecHeader = 0;
	char CharBuffer [STRSIZE];
	char *next = "";		/* Not needed */
	int c;
	
	if ((f = fopen (file, "r"))==NULL)
		return NULL;
	
	state = FirstBrace;
	while ((c = getc (f)) != EOF){
		if (c == '\r')		/* Ignore Carriage Return */
			continue;
		
		switch (state){
			
		case OnSecHeader:
			if (c == ']' || overflow){
				*next = '\0';
				next = CharBuffer;
				SecHeader->section_name = g_strdup (CharBuffer);
				state = IgnoreToEOL;
			} else
				*next++ = c;
			break;

		case IgnoreToEOL:
			if (c == '\n'){
				state = KeyDef;
				next = CharBuffer;
			}
			break;

		case FirstBrace:
		case KeyDef:
		case KeyDefOnKey:
			if (c == '[' && state != KeyDefOnKey){
				TSecHeader *temp;
		
				temp = SecHeader;
				SecHeader = (TSecHeader *) g_malloc (sizeof (TSecHeader));
				SecHeader->link = temp;
				SecHeader->keys = 0;
				state = OnSecHeader;
				next = CharBuffer;
				break;
			}
			/* On first pass, don't allow dangling keys */
			if (state == FirstBrace)
				break;
	    
			if ((c == ' ' && state != KeyDefOnKey) || c == '\t')
				break;
	    
			if (c == '\n' || overflow) { /* Abort Definition */
				next = CharBuffer;
                                break;
                        }
	    
			if (c == '=' || overflow){
				TKeys *temp;

				temp = SecHeader->keys;
				*next = '\0';
				SecHeader->keys = (TKeys *) g_malloc (sizeof (TKeys));
				SecHeader->keys->link = temp;
				SecHeader->keys->key_name = g_strdup (CharBuffer);
				state = KeyValue;
				next = CharBuffer;
			} else {
				*next++ = c;
				state = KeyDefOnKey;
			}
			break;

		case KeyValue:
			if (overflow || c == '\n'){
				*next = '\0';
				SecHeader->keys->value = decode_string_and_dup (CharBuffer);
				state = c == '\n' ? KeyDef : IgnoreToEOL;
				next = CharBuffer;
#ifdef GNOME_ENABLE_DEBUG
				printf ("[%s] (%s)=%s\n", SecHeader->section_name,
					SecHeader->keys->key_name, SecHeader->keys->value);
#endif
			} else
				*next++ = c;
			break;
	    
		} /* switch */
	
	} /* while ((c = getc (f)) != EOF) */
	if (c == EOF && state == KeyValue){
		*next = '\0';
		SecHeader->keys->value = decode_string_and_dup (CharBuffer);
	}
	fclose (f);
	return SecHeader;
}

static void 
new_key (TSecHeader *section, const char *key_name, const char *value)
{
	TKeys *key;
    
	key = (TKeys *) g_malloc (sizeof (TKeys));
	key->key_name = g_strdup (key_name);
	key->value   = g_strdup (value);
	key->link = section->keys;
	section->keys = key;
}

static const char *
access_config (access_type mode, const char *section_name,
	       const char *key_name, const char *def, const char *filename,
	       gboolean *def_used)
{
    
	TProfile   *New;
	TSecHeader *section;
	TKeys      *key;

	if (def_used)
		*def_used = FALSE;
	if (!is_loaded (filename, &section)){
		struct stat st;
		if (stat (filename, &st) == -1) st.st_mtime = 0;

		New = (TProfile *) g_malloc (sizeof (TProfile));
		New->link = Base;
		New->filename = g_strdup (filename);
		New->section = load (filename);
		New->mtime = st.st_mtime;
		New->written_to = FALSE;
		New->to_be_deleted = FALSE;
		Base = New;
		section = New->section;
		Current = New;
	}
    
	/* Start search */
	for (; section; section = section->link){
		if (section->section_name == 0)
			continue;
		if (strcasecmp (section->section_name, section_name))
			continue;
		
		for (key = section->keys; key; key = key->link){
			if (strcasecmp (key->key_name, key_name))
				continue;
			if (mode == SET){
				g_free (key->value);
				key->value = g_strdup (def);
				Current->written_to = TRUE;
			}
			return key->value;
		}

		/* No key found */
		if (mode == SET){
			new_key (section, key_name, def);
			Current->written_to = TRUE;
			return 0;
		}
	}
    
	/* Non existent section */
	if ((mode == SET) && def){
		section = (TSecHeader *) g_malloc (sizeof (TSecHeader));
		section->section_name = g_strdup (section_name);
		section->keys = 0;
		new_key (section, key_name, def);
		section->link = Current->section;
		Current->section = section;
		Current->written_to = TRUE;
	} 
	if (def_used)
		*def_used = TRUE;
	return def;
}

static void 
dump_keys (FILE *profile, TKeys *p)
{
	if (!p)
		return;
	dump_keys (profile, p->link);
	if (*p->key_name) {
		char *t = escape_string_and_dup (p->value);
		fprintf (profile, "%s=%s\n", p->key_name, t);
		g_free (t);
	}
}

static void 
dump_sections (FILE *profile, TSecHeader *p)
{
	if (!p)
		return;
	dump_sections (profile, p->link);
	if (p->section_name && p->section_name [0]){
		fprintf (profile, "\n[%s]\n", p->section_name);
		dump_keys (profile, p->keys);
	}
}

/*check the path and if we need to create directories create them with
  mode newmode, it needs an absolute path name or it will fail, it
  needs to be passed the dir and the filename since it will take the
  filename off*/
static gint
check_path(char *path, mode_t newmode)
{
	gchar *dirpath;
	gchar *p;
	GString *newpath;
	struct stat s;


	g_return_val_if_fail (path != NULL, FALSE);

	if(strchr(path,'/')==NULL)
		return FALSE;

	dirpath = strcpy (alloca (strlen (path) + 1), path);
	g_return_val_if_fail (dirpath != NULL, FALSE);

	if (*dirpath == '\0')
		return FALSE;

	/*not absolute, we refuse to work*/
	if (dirpath[0] != '/')
		return FALSE;

	p = strrchr(dirpath,'/');
		*p='\0';

	/*special case if directory exists, this is probably gonna happen
	  a lot so we don't want to go though checking it part by part*/
	if (stat(dirpath, &s) == 0) {
		/*check if a directory*/
		if (!S_ISDIR(s.st_mode))
			return FALSE;
		else
			return TRUE;
	}


	/*skip leading '/'*/
	p = dirpath;
	while(*p == '/')
		p++;

	p = strtok(p, "/");
	newpath = g_string_new("");
	do {
		newpath = g_string_append_c(newpath,'/');
		newpath = g_string_append(newpath,p);
		if(stat(newpath->str,&s)==0) {
			/*check if a directory*/
			if(!S_ISDIR(s.st_mode)) {
				g_string_free(newpath,TRUE);
				return FALSE;
			}
		} else {
			/*we couldn't stat it .. let's try making the
			  directory*/
			if(mkdir(newpath->str,newmode)!=0) {
				/*error, return false*/
				g_string_free(newpath,TRUE);
				return FALSE;
			}
		}

	} while ((p = strtok(NULL, "/")) != NULL);

	g_string_free(newpath,TRUE);

	return TRUE;
}

static void 
free_keys (TKeys *p)
{
	if (!p)
		return;
	free_keys (p->link);
	g_free (p->key_name);
	g_free (p->value);
	g_free (p);
}

static void 
free_sections (TSecHeader *p)
{
	if (!p)
		return;
	free_sections (p->link);
	free_keys (p->keys);
	g_free (p->section_name);
	p->link = 0;
	p->keys = 0;
	g_free (p);
}

static void 
free_profile (TProfile *p)
{
	if (!p)
		return;
	if(Current == p)
		Current = NULL;
	free_profile (p->link);
	free_sections (p->section);
	g_free (p->filename);
	g_free (p);
}


static void 
dump_profile (TProfile *p)
{
	FILE *profile;
    
	if (!p)
		return;
	dump_profile (p->link);
	
	/*
	 * was this profile written to?, if not it's not necessary to dump
	 * it to disk
	 */
	if (!p->to_be_deleted)
		if(!p->written_to)
			return;
	

	/* .ado: p->filename can be empty, it's better to jump over */
	if (p->filename[0] != (char) 0) {

		/*
		 * this file was added to after it was cleaned so it doesn't
		 * want to be deleted
		 */
		if(p->to_be_deleted && p->section)
			p->to_be_deleted = FALSE;
		if(p->to_be_deleted) {
			/*remove the file and remove all it's ramaints
			  from memory*/
			unlink(p->filename);
			/* this already must have been true */
			/*p->section = 0;*/
			p->filename [0] = 0;
			p->written_to = TRUE;
			p->to_be_deleted = FALSE;
			if(p==Current)
				Current = NULL;
		} else if (check_path(p->filename,0755) &&
		    (profile = fopen (p->filename, "w")) != NULL){
			dump_sections (profile, p->section);
			fclose (profile);
		}
	}
	
	/*mark this to not be dumped any more*/
	p->written_to = FALSE;
}

/**
 * gnome_config_sync:
 *
 * Writes all of the information modified by gnome-config to the
 * disk.
 *
 * Note: the gnome_config code does not write anything to the
 * configuration files until this routine is actually invoked.
 */
void 
gnome_config_sync (void)
{
	dump_profile (Base);
	CALL_SYNC_HANDLER();
}

/**
 * gnome_config_clean_file: 
 * @path: A gnome-config path
 *
 * Cleans up the configuration file specified by @path from any
 * configuration information.
 *
 * Changes will take place after gnome_config_sync has been invoked.
 */
/**
 * gnome_config_private_clean_file:
 * @path: A gnome-config path
 *
 * Cleans up the private configuration file specified by @path from
 * any configuration information.
 *
 * Changes will take place after gnome_config_sync has been invoked.
 */
void 
_gnome_config_clean_file (const char *path, gint priv)
{
	TProfile *p;
	ParsedPath *pp;
	char *fake_path;
	
	if (!path)
		return;

	fake_path = g_strconcat (path, "/section/key", NULL);
	pp = parse_path (fake_path, priv);
	g_free (fake_path);

	/* this is the current so don't look no further*/
	if (Current && strcmp (pp->file, Current->filename) == 0) {
		free_sections (Current->section);
		Current->section = 0;
		Current->written_to = TRUE;
		Current->to_be_deleted = TRUE;
		Current = NULL;
		release_path (pp);
		return;
	}
	
	for (p = Base; p; p = p->link){
		if (strcmp (pp->file, p->filename) != 0)
			continue;
		
		free_sections (p->section);
		p->section = 0;
		p->written_to = TRUE;
		p->to_be_deleted = TRUE;
		release_path (pp);
		return;
	}
	release_path (pp);
}

/**
 * gnome_config_drop_file: 
 * @path: A gnome-config path
 *
 * Releases any memory resources that were allocated from accessing
 * the configuration file in @path.  Changes will take place after
 * gnome_config_sync has been invoked
 */
/**
 * gnome_config_private_drop_file:
 * @path: A gnome-config path
 *
 * Releases any memory resources that were allocated from accessing the
 * private configuration file in @path.
 */
void 
_gnome_config_drop_file (const char *path, gint priv)
{
	TProfile *p;
	TProfile *last;
	ParsedPath *pp;
	char *fake_path;
	
	if (!path)
		return;

	fake_path = g_strconcat (path, "/section/key", NULL);
	pp = parse_path (fake_path, priv);
	g_free (fake_path);

	/*this is the current so don't look no further*/
	if (Current && strcmp (pp->file, Current->filename) == 0) {
		free_sections (Current->section);
		Current->section = 0;
		Current->filename [0] = 0;
		Current->written_to = TRUE;
		Current->to_be_deleted = FALSE;
		Current = NULL;
		release_path (pp);
		return;
	}
	
	for (last = NULL,p = Base; p; last = p, p = p->link){
		if (strcmp (pp->file, p->filename) != 0)
			continue;
		
		if(last)
			last->link = p->link;
		else
			Base = p->link;
		
		free_sections (p->section);
		g_free(p->filename);
		g_free(p);
		release_path (pp);
		return;
	}
	release_path (pp);
}

/**
 * gnome_config_init_iterator:
 * @path: A gnome configuration path for a section.
 *
 * Creates an iterator handle that can be used to
 * iterate over the keys in a section in a gnome configuration
 * file.  @path must refer to a section.  The returned value
 * can be used as an iterator for gnome_config_iterator_next().
 *
 * Returns the iterator handle.
 */
/**
 * gnome_config_private_init_iterator:
 * @path: A gnome configuration path for a section.
 *
 * Creates an iterator handle that can be used to
 * iterate over the keys in a section in a private gnome configuration
 * file.  @path must refer to a section.  The returned value
 * can be used as an iterator for gnome_config_iterator_next().
 *
 * Returns the iterator handle.
 */
void *
_gnome_config_init_iterator (const char *path, gint priv)
{
	TProfile   *New;
	TSecHeader *section;
	ParsedPath *pp;
	char *fake_path;
	iterator_type *iter;


	fake_path = g_concat_dir_and_file (path, "key");
	pp = parse_path (fake_path, priv);
	g_free (fake_path);
	
	if (!is_loaded (pp->file, &section)){
		struct stat st;
		if (stat (pp->file, &st) == -1) st.st_mtime = 0;

		New = (TProfile *) g_malloc (sizeof (TProfile));
		New->link = Base;
		New->filename = g_strdup (pp->file);
		New->section = load (pp->file);
		New->mtime = st.st_mtime;
		New->written_to = FALSE;
		New->to_be_deleted = FALSE;
		Base = New;
		section = New->section;
		Current = New;
	}
	for (; section; section = section->link){
		if (strcasecmp (section->section_name, pp->section))
			continue;
		iter = g_new (iterator_type, 1);
		iter->type = 0;
		iter->value = section->keys;
		release_path (pp);
		return iter;
	}
	release_path (pp);
	return 0;
}


/**
 * gnome_config_init_iterator_sections:
 * @path: A gnome configuration path for a file.
 *
 * Creates an iterator handle that can be used to iterate over the
 * sections in a gnome configuration file.  @path must refer to a
 * gnome configuration file.  The returned value can be used as an
 * iterator for gnome_config_iterator_next().
 *
 * Returns the iterator handle.
 */
/**
 * gnome_config_private_init_iterator_sections:
 * @path: A gnome configuration path for a file
 *
 * Creates an iterator handle that can be used to iterate over the
 * sections in a private gnome configuration file.  @path must refer to a
 * gnome configuration file.  The returned value can be used as an
 * iterator for gnome_config_iterator_next().
 *
 * Returns the iterator handle.
 */
void *
_gnome_config_init_iterator_sections (const char *path, gint priv)
{
	TProfile   *New;
	TSecHeader *section;
	ParsedPath *pp;
	char *fake_path;
	iterator_type *iter;


	fake_path = g_strconcat (path, "/section/key", NULL);<commit_msg>for _file methods accept files ending in '/'<commit_after>/*
 * Configuration-File Functions.
 *
 *  Copyright 1993, 1994, 1997 The Free Software Foundation
 *
 * Authors: <NAME>

   This program is free software; you can redistribute it and/or
   modify it under the terms of the GNU Library General Public License
   as published by the Free Software Foundation; either version 2 of
   the License, or (at your option) any later version.
   
   The Gnome Library 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 Library General Public
   License along with the Gnome Library; see the file COPYING.LIB.  If not,
   write to the Free Software Foundation, Inc., 59 Temple Place - Suite 330,
   Boston, MA 02111-1307, USA.  */

#include <config.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>	/* unlink() */
#include <stdlib.h>	/* atoi() */
#include <sys/types.h>
#include <sys/stat.h>
#include <glib.h>
#include "libgnomeP.h"

#ifdef HAVE_STRNDUP
#include <string.h>
#else
/* Like strdup, but only copy N chars.  */
extern char *strndup (const char *s, size_t n);
#endif

#define STRSIZE 4096
#define overflow (next == &CharBuffer [STRSIZE-1])

enum {
	FirstBrace,
	OnSecHeader,
	IgnoreToEOL,
	KeyDef,
	KeyDefOnKey,
	KeyValue
};

typedef struct {
	int type;
	void *value;
} iterator_type;

typedef enum {
	LOOKUP,
	SET
} access_type;

typedef struct TKeys {
	char *key_name;
	char *value;
	struct TKeys *link;
} TKeys;

typedef struct TSecHeader {
	char *section_name;
	TKeys *keys;
	struct TSecHeader *link;
} TSecHeader;

typedef struct TProfile {
	char *filename;
	time_t mtime;
	int written_to;
	int to_be_deleted;
	TSecHeader *section;
	struct TProfile *link;
} TProfile;

/*
 * Prefix for all the configuration operations
 * iff the path does not begin with / or with #
 */

#define prefix (prefix_list ? prefix_list->data : NULL)

static GSList *prefix_list = NULL;

static TProfile *Current = 0;

/*
 * This one keeps track of all of the opened files
 */
static TProfile *Base = 0;

/*
 * a set handler is a function which is called every time a gnome_config_set_*
 * function is called, this can be used by the app to say guarantee a sync,
 * apps using libgnomeui should not call this, libgnomeui already provides
 * this, this would be for non-gui apps and apps that use a different toolkit
 * than gtk.
 */
static void (*set_handler)(void *data) = NULL;
static void *set_handler_data;

#define CALL_SET_HANDLER() { if(set_handler) (*set_handler)(set_handler_data); }

/* same as above for a "sync" handler */
static void (*sync_handler)(void *data) = NULL;
static void *sync_handler_data;

#define CALL_SYNC_HANDLER() { if(sync_handler) (*sync_handler)(sync_handler_data); }



/* The `release_path' and `parsed_path' routines are inside the
   following file.  It is in a separate file to allow the test-suite
   to get at it, without needing to export special symbols.

   typedef struct {
	char *file, *section, *key, *def;
	char *path, *opath;
   } ParsedPath;

   static void release_path (ParsedPath *p);
   static ParsedPath *parse_path (const char *path, gint priv); */
#include "parse-path.cP"

static int 
is_loaded (const char *filename, TSecHeader **section)
{
	TProfile *p = Base;
	TProfile *lastp = NULL;
	struct stat st;
	
	/*if the last one we accessed was this one we don't want to
	  search*/
	if(Current && strcasecmp (filename, Current->filename) == 0){
		if (stat (filename, &st) == -1)
			st.st_mtime = 0;
		if (Current->mtime != st.st_mtime)
			return 0;
		*section = Current->section;
		return 1;
	}
	
	while (p){
		/*search and destroy empty nodes*/
		if (p->filename[0]=='\0') {
			TProfile *next = p->link;
			if(lastp)
				lastp->link = next;
			else /*the next one is the first one actually*/
				Base = next;
			g_free(p->filename);
			g_free(p);
			p = next;
		} else if (strcasecmp (filename, p->filename) == 0){
			if (stat (filename, &st) == -1)
				st.st_mtime = 0;
			if (p->mtime != st.st_mtime)
				return 0;
			Current = p;
			*section = p->section;
			return 1;
		} else {
			lastp = p;
			p = p->link;
		}
	}
	return 0;
}

static char *
decode_string_and_dup (char *s)
{
	char *p = g_malloc (strlen (s) + 1);
	char *q = p;

	do {
		if (*s == '\\'){
			switch (*(++s)){
			case 'n':
				*p++ = '\n';
				break;
			case '\\':
				*p++ = '\\';
				break;
			case 'r':
				*p++ = '\r';
				break;
			default:
				*p++ = '\\';
				*p++ = *s;
			}
		} else
			*p++ = *s;
	} while (*s++);
	return q;
}

static char *
escape_string_and_dup (char *s)
{
	char *return_value, *p = s;
	int len = 0;

	if(!s)
		return g_strdup("");
	
	while (*p){
		len++;
		if (*p == '\n' || *p == '\\' || *p == '\r' || *p == '\0')
			len++;
		p++;
	}
	return_value = p = (char *) g_malloc (len + 1);
	if (!return_value)
		return 0;
	do {
		switch (*s){
		case '\n':
			*p++ = '\\';
			*p++ = 'n';
			break;
		case '\r':
			*p++ = '\\';
			*p++ = 'r';
			break;
		case '\\':
			*p++ = '\\';
			*p++ = '\\';
			break;
		default:
			*p++ = *s;
		}
	} while (*s++);
	return return_value;
}

static TSecHeader *
load (const char *file)
{
	FILE *f;
	int state;
	TSecHeader *SecHeader = 0;
	char CharBuffer [STRSIZE];
	char *next = "";		/* Not needed */
	int c;
	
	if ((f = fopen (file, "r"))==NULL)
		return NULL;
	
	state = FirstBrace;
	while ((c = getc (f)) != EOF){
		if (c == '\r')		/* Ignore Carriage Return */
			continue;
		
		switch (state){
			
		case OnSecHeader:
			if (c == ']' || overflow){
				*next = '\0';
				next = CharBuffer;
				SecHeader->section_name = g_strdup (CharBuffer);
				state = IgnoreToEOL;
			} else
				*next++ = c;
			break;

		case IgnoreToEOL:
			if (c == '\n'){
				state = KeyDef;
				next = CharBuffer;
			}
			break;

		case FirstBrace:
		case KeyDef:
		case KeyDefOnKey:
			if (c == '[' && state != KeyDefOnKey){
				TSecHeader *temp;
		
				temp = SecHeader;
				SecHeader = (TSecHeader *) g_malloc (sizeof (TSecHeader));
				SecHeader->link = temp;
				SecHeader->keys = 0;
				state = OnSecHeader;
				next = CharBuffer;
				break;
			}
			/* On first pass, don't allow dangling keys */
			if (state == FirstBrace)
				break;
	    
			if ((c == ' ' && state != KeyDefOnKey) || c == '\t')
				break;
	    
			if (c == '\n' || overflow) { /* Abort Definition */
				next = CharBuffer;
                                break;
                        }
	    
			if (c == '=' || overflow){
				TKeys *temp;

				temp = SecHeader->keys;
				*next = '\0';
				SecHeader->keys = (TKeys *) g_malloc (sizeof (TKeys));
				SecHeader->keys->link = temp;
				SecHeader->keys->key_name = g_strdup (CharBuffer);
				state = KeyValue;
				next = CharBuffer;
			} else {
				*next++ = c;
				state = KeyDefOnKey;
			}
			break;

		case KeyValue:
			if (overflow || c == '\n'){
				*next = '\0';
				SecHeader->keys->value = decode_string_and_dup (CharBuffer);
				state = c == '\n' ? KeyDef : IgnoreToEOL;
				next = CharBuffer;
#ifdef GNOME_ENABLE_DEBUG
				printf ("[%s] (%s)=%s\n", SecHeader->section_name,
					SecHeader->keys->key_name, SecHeader->keys->value);
#endif
			} else
				*next++ = c;
			break;
	    
		} /* switch */
	
	} /* while ((c = getc (f)) != EOF) */
	if (c == EOF && state == KeyValue){
		*next = '\0';
		SecHeader->keys->value = decode_string_and_dup (CharBuffer);
	}
	fclose (f);
	return SecHeader;
}

static void 
new_key (TSecHeader *section, const char *key_name, const char *value)
{
	TKeys *key;
    
	key = (TKeys *) g_malloc (sizeof (TKeys));
	key->key_name = g_strdup (key_name);
	key->value   = g_strdup (value);
	key->link = section->keys;
	section->keys = key;
}

static const char *
access_config (access_type mode, const char *section_name,
	       const char *key_name, const char *def, const char *filename,
	       gboolean *def_used)
{
    
	TProfile   *New;
	TSecHeader *section;
	TKeys      *key;

	if (def_used)
		*def_used = FALSE;
	if (!is_loaded (filename, &section)){
		struct stat st;
		if (stat (filename, &st) == -1) st.st_mtime = 0;

		New = (TProfile *) g_malloc (sizeof (TProfile));
		New->link = Base;
		New->filename = g_strdup (filename);
		New->section = load (filename);
		New->mtime = st.st_mtime;
		New->written_to = FALSE;
		New->to_be_deleted = FALSE;
		Base = New;
		section = New->section;
		Current = New;
	}
    
	/* Start search */
	for (; section; section = section->link){
		if (section->section_name == 0)
			continue;
		if (strcasecmp (section->section_name, section_name))
			continue;
		
		for (key = section->keys; key; key = key->link){
			if (strcasecmp (key->key_name, key_name))
				continue;
			if (mode == SET){
				g_free (key->value);
				key->value = g_strdup (def);
				Current->written_to = TRUE;
			}
			return key->value;
		}

		/* No key found */
		if (mode == SET){
			new_key (section, key_name, def);
			Current->written_to = TRUE;
			return 0;
		}
	}
    
	/* Non existent section */
	if ((mode == SET) && def){
		section = (TSecHeader *) g_malloc (sizeof (TSecHeader));
		section->section_name = g_strdup (section_name);
		section->keys = 0;
		new_key (section, key_name, def);
		section->link = Current->section;
		Current->section = section;
		Current->written_to = TRUE;
	} 
	if (def_used)
		*def_used = TRUE;
	return def;
}

static void 
dump_keys (FILE *profile, TKeys *p)
{
	if (!p)
		return;
	dump_keys (profile, p->link);
	if (*p->key_name) {
		char *t = escape_string_and_dup (p->value);
		fprintf (profile, "%s=%s\n", p->key_name, t);
		g_free (t);
	}
}

static void 
dump_sections (FILE *profile, TSecHeader *p)
{
	if (!p)
		return;
	dump_sections (profile, p->link);
	if (p->section_name && p->section_name [0]){
		fprintf (profile, "\n[%s]\n", p->section_name);
		dump_keys (profile, p->keys);
	}
}

/*check the path and if we need to create directories create them with
  mode newmode, it needs an absolute path name or it will fail, it
  needs to be passed the dir and the filename since it will take the
  filename off*/
static gint
check_path(char *path, mode_t newmode)
{
	gchar *dirpath;
	gchar *p;
	GString *newpath;
	struct stat s;


	g_return_val_if_fail (path != NULL, FALSE);

	if(strchr(path,'/')==NULL)
		return FALSE;

	dirpath = strcpy (alloca (strlen (path) + 1), path);
	g_return_val_if_fail (dirpath != NULL, FALSE);

	if (*dirpath == '\0')
		return FALSE;

	/*not absolute, we refuse to work*/
	if (dirpath[0] != '/')
		return FALSE;

	p = strrchr(dirpath,'/');
		*p='\0';

	/*special case if directory exists, this is probably gonna happen
	  a lot so we don't want to go though checking it part by part*/
	if (stat(dirpath, &s) == 0) {
		/*check if a directory*/
		if (!S_ISDIR(s.st_mode))
			return FALSE;
		else
			return TRUE;
	}


	/*skip leading '/'*/
	p = dirpath;
	while(*p == '/')
		p++;

	p = strtok(p, "/");
	newpath = g_string_new("");
	do {
		newpath = g_string_append_c(newpath,'/');
		newpath = g_string_append(newpath,p);
		if(stat(newpath->str,&s)==0) {
			/*check if a directory*/
			if(!S_ISDIR(s.st_mode)) {
				g_string_free(newpath,TRUE);
				return FALSE;
			}
		} else {
			/*we couldn't stat it .. let's try making the
			  directory*/
			if(mkdir(newpath->str,newmode)!=0) {
				/*error, return false*/
				g_string_free(newpath,TRUE);
				return FALSE;
			}
		}

	} while ((p = strtok(NULL, "/")) != NULL);

	g_string_free(newpath,TRUE);

	return TRUE;
}

static void 
free_keys (TKeys *p)
{
	if (!p)
		return;
	free_keys (p->link);
	g_free (p->key_name);
	g_free (p->value);
	g_free (p);
}

static void 
free_sections (TSecHeader *p)
{
	if (!p)
		return;
	free_sections (p->link);
	free_keys (p->keys);
	g_free (p->section_name);
	p->link = 0;
	p->keys = 0;
	g_free (p);
}

static void 
free_profile (TProfile *p)
{
	if (!p)
		return;
	if(Current == p)
		Current = NULL;
	free_profile (p->link);
	free_sections (p->section);
	g_free (p->filename);
	g_free (p);
}


static void 
dump_profile (TProfile *p)
{
	FILE *profile;
    
	if (!p)
		return;
	dump_profile (p->link);
	
	/*
	 * was this profile written to?, if not it's not necessary to dump
	 * it to disk
	 */
	if (!p->to_be_deleted)
		if(!p->written_to)
			return;
	

	/* .ado: p->filename can be empty, it's better to jump over */
	if (p->filename[0] != (char) 0) {

		/*
		 * this file was added to after it was cleaned so it doesn't
		 * want to be deleted
		 */
		if(p->to_be_deleted && p->section)
			p->to_be_deleted = FALSE;
		if(p->to_be_deleted) {
			/*remove the file and remove all it's ramaints
			  from memory*/
			unlink(p->filename);
			/* this already must have been true */
			/*p->section = 0;*/
			p->filename [0] = 0;
			p->written_to = TRUE;
			p->to_be_deleted = FALSE;
			if(p==Current)
				Current = NULL;
		} else if (check_path(p->filename,0755) &&
		    (profile = fopen (p->filename, "w")) != NULL){
			dump_sections (profile, p->section);
			fclose (profile);
		}
	}
	
	/*mark this to not be dumped any more*/
	p->written_to = FALSE;
}

/**
 * gnome_config_sync:
 *
 * Writes all of the information modified by gnome-config to the
 * disk.
 *
 * Note: the gnome_config code does not write anything to the
 * configuration files until this routine is actually invoked.
 */
void 
gnome_config_sync (void)
{
	dump_profile (Base);
	CALL_SYNC_HANDLER();
}

/**
 * gnome_config_clean_file: 
 * @path: A gnome-config path
 *
 * Cleans up the configuration file specified by @path from any
 * configuration information.
 *
 * Changes will take place after gnome_config_sync has been invoked.
 */
/**
 * gnome_config_private_clean_file:
 * @path: A gnome-config path
 *
 * Cleans up the private configuration file specified by @path from
 * any configuration information.
 *
 * Changes will take place after gnome_config_sync has been invoked.
 */
void 
_gnome_config_clean_file (const char *path, gint priv)
{
	TProfile *p;
	ParsedPath *pp;
	char *fake_path;
	
	if (!path)
		return;

	fake_path = g_concat_dir_and_file (path, "section/key");
	pp = parse_path (fake_path, priv);
	g_free (fake_path);

	/* this is the current so don't look no further*/
	if (Current && strcmp (pp->file, Current->filename) == 0) {
		free_sections (Current->section);
		Current->section = 0;
		Current->written_to = TRUE;
		Current->to_be_deleted = TRUE;
		Current = NULL;
		release_path (pp);
		return;
	}
	
	for (p = Base; p; p = p->link){
		if (strcmp (pp->file, p->filename) != 0)
			continue;
		
		free_sections (p->section);
		p->section = 0;
		p->written_to = TRUE;
		p->to_be_deleted = TRUE;
		release_path (pp);
		return;
	}
	release_path (pp);
}

/**
 * gnome_config_drop_file: 
 * @path: A gnome-config path
 *
 * Releases any memory resources that were allocated from accessing
 * the configuration file in @path.  Changes will take place after
 * gnome_config_sync has been invoked
 */
/**
 * gnome_config_private_drop_file:
 * @path: A gnome-config path
 *
 * Releases any memory resources that were allocated from accessing the
 * private configuration file in @path.
 */
void 
_gnome_config_drop_file (const char *path, gint priv)
{
	TProfile *p;
	TProfile *last;
	ParsedPath *pp;
	char *fake_path;
	
	if (!path)
		return;

	fake_path = g_concat_dir_and_file (path, "section/key");
	pp = parse_path (fake_path, priv);
	g_free (fake_path);

	/*this is the current so don't look no further*/
	if (Current && strcmp (pp->file, Current->filename) == 0) {
		free_sections (Current->section);
		Current->section = 0;
		Current->filename [0] = 0;
		Current->written_to = TRUE;
		Current->to_be_deleted = FALSE;
		Current = NULL;
		release_path (pp);
		return;
	}
	
	for (last = NULL,p = Base; p; last = p, p = p->link){
		if (strcmp (pp->file, p->filename) != 0)
			continue;
		
		if(last)
			last->link = p->link;
		else
			Base = p->link;
		
		free_sections (p->section);
		g_free(p->filename);
		g_free(p);
		release_path (pp);
		return;
	}
	release_path (pp);
}

/**
 * gnome_config_init_iterator:
 * @path: A gnome configuration path for a section.
 *
 * Creates an iterator handle that can be used to
 * iterate over the keys in a section in a gnome configuration
 * file.  @path must refer to a section.  The returned value
 * can be used as an iterator for gnome_config_iterator_next().
 *
 * Returns the iterator handle.
 */
/**
 * gnome_config_private_init_iterator:
 * @path: A gnome configuration path for a section.
 *
 * Creates an iterator handle that can be used to
 * iterate over the keys in a section in a private gnome configuration
 * file.  @path must refer to a section.  The returned value
 * can be used as an iterator for gnome_config_iterator_next().
 *
 * Returns the iterator handle.
 */
void *
_gnome_config_init_iterator (const char *path, gint priv)
{
	TProfile   *New;
	TSecHeader *section;
	ParsedPath *pp;
	char *fake_path;
	iterator_type *iter;


	fake_path = g_concat_dir_and_file (path, "key");
	pp = parse_path (fake_path, priv);
	g_free (fake_path);
	
	if (!is_loaded (pp->file, &section)){
		struct stat st;
		if (stat (pp->file, &st) == -1) st.st_mtime = 0;

		New = (TProfile *) g_malloc (sizeof (TProfile));
		New->link = Base;
		New->filename = g_strdup (pp->file);
		New->section = load (pp->file);
		New->mtime = st.st_mtime;
		New->written_to = FALSE;
		New->to_be_deleted = FALSE;
		Base = New;
		section = New->section;
		Current = New;
	}
	for (; section; section = section->link){
		if (strcasecmp (section->section_name, pp->section))
			continue;
		iter = g_new (iterator_type, 1);
		iter->type = 0;
		iter->value = section->keys;
		release_path (pp);
		return iter;
	}
	release_path (pp);
	return 0;
}


/**
 * gnome_config_init_iterator_sections:
 * @path: A gnome configuration path for a file.
 *
 * Creates an iterator handle that can be used to iterate over the
 * sections in a gnome configuration file.  @path must refer to a
 * gnome configuration file.  The returned value can be used as an
 * iterator for gnome_config_iterator_next().
 *
 * Returns the iterator handle.
 */
/**
 * gnome_config_private_init_iterator_sections:
 * @path: A gnome configuration path for a file
 *
 * Creates an iterator handle that can be used to iterate over the
 * sections in a private gnome configuration file.  @path must refer to a
 * gnome configuration file.  The returned value can be used as an
 * iterator for gnome_config_iterator_next().
 *
 * Returns the iterator handle.
 */
void *
_gnome_config_init_iterator_sections (const char *path, gint priv)
{
	TProfile   *New;
	TSecHeader *section;
	ParsedPath *pp;
	char *fake_path;
	iterator_type *iter;


	fake_path = g_concat_dir_and_file (path, "section/key");
{'fraction_non_alphanumeric': 0.14202688949595585, 'fraction_numerical': 0.005142214366061385, 'mean_word_length': 3.2072112676056337, 'pattern_counts': {'":': 0, '<': 23, '<?xml version=': 0, '>': 311, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '1037675', 'n_tokens_mistral': 14821, 'n_tokens_neox': 13268, 'n_words': 4131}