IniFile

Discussion of the xtools Toolkit

Moderators: Tom, Kukurykus

xbytor

IniFile

Post by xbytor »

Here's my code for working with ini files. I use this all the time and it works well for me. Enjoy!
Code: Select all//
// IniFile.jsx
//
// $Id: IniFile.jsx,v 1.1 2006/11/21 21:15:09 anonymous Exp $
// Contact: xbytor@gmail.com
//

//
// IniFile is a set of functions for reading and writing ini files
// in a consistent fashion across a broad number of scripts.
//
function IniFile(fptr) {
};

//
// Return fptr if its a File or Folder, if not, make it one
//
IniFile.convertFptr = function(fptr) {
  var f;
  if (fptr.constructor == String) {
    f = File(fptr);
  } else if (fptr instanceof File || fptr instanceof Folder) {
    f = fptr;
  } else {
    throw IOError("Bad file \"" + fptr + "\" specified.");
  }
  return f;
};

//
// Return an ini string to an object. Use 'ini' as the object if it's specified
//
IniFile.iniFromString = function(str, ini) {
  var lines = str.split(/\r|\n/);
  var rexp = new RegExp(/([^:]+):(.*)$/);

  if (!ini) {
    ini = {};
  }
   
  for (var i = 0; i < lines.length; i++) {
    var line = IniFile.trim(lines);
    if (!line || line.charAt(0) == '#') {
      continue;
    }
    var ar = rexp.exec(line);
    if (!ar) {
      alert("Bad line in config: \"" + line + "\"");
      return undefined;
    }
    ini[IniFile.trim(ar[1])] = IniFile.trim(ar[2]);
  }

  return ini;
};


//
// Return an ini file to an object. Use 'ini' as the object if it's specified
//
IniFile.read = function(iniFile, ini) {
  if (!ini) {
    ini = {};
  }
  if (!iniFile) {
    return ini;
  }
  var file = IniFile.convertFptr(iniFile);

  if (!file) {
    throw "Bad ini file specified: \"" + iniFile + "\".";
  }

  if (!file.exists) {
  }
  if (file.exists && file.open("r")) {
    var str = file.read();
    ini = IniFile.iniFromString(str, ini);
    file.close();
  }
  return ini;
};

//
// Return an ini string coverted from an object
//
IniFile.iniToString = function(ini) {
  var str = '';
  for (var idx in ini) {
    if (idx.charAt(0) == '_') {         // private stuff
      continue;
    }
    if (idx == 'typename') {
      continue;
    }
    var val = ini[idx];
   
    if (val.constructor == String ||
        val.constructor == Number ||
        val.constructor == Boolean ||
        typeof(val) == "object") {
      str += (idx + ": " + val.toString() + "\n");
    }
  }
  return str;
};

//
// Write an object to an ini file overwriting whatever was there before
//
IniFile.overwrite = function(iniFile, ini) {
  if (!ini || !iniFile) {
    return;
  }
  var file = IniFile.convertFptr.iniFileToFile(iniFile);

  if (!file) {
    throw "Bad ini file specified: \"" + iniFile + "\".";
  }
 
  if (!file.open("w")) {
    throw "Unable to open ini file " + file + ": " + file.error;
  }

  var str = IniString.iniToString(ini);
  file.write(str);
  file.close();

  return ini;
};
IniFile.trim = function(value) {
   return value.replace(/^[\s]+|[\s]+$/g, '');
};


//
// Updating the ini file retains the ini file layout including any externally
// add comments, blank lines, and the property sequence
//
IniFile.update = function(iniFile, ini) {
  if (!ini || !iniFile) {
    return;
  }
  var file = IniFile.convertFptr(iniFile);

  // we can only update the file if it exists
  var update = file.exists;
  var str = '';

  if (update) {
    file.open("r");
    str = file.read();
    file.close();
   
    for (var idx in ini) {
      if (idx.charAt(0) == '_') {         // private stuff
        continue;
      }
      if (idx == "typename") {
        continue;
      }

      var val = ini[idx];

      if (val == undefined) {
        val = '';
      }
     
      if (typeof val == "string" ||
          typeof val == "number" ||
          typeof val == "boolean" ||
          typeof val == "object") {
        idx += ':';
        var re = RegExp('^' + idx, 'm');

        if (re.test(str)) {
          re = RegExp('^' + idx + '[^\n]+', 'm');
          str = str.replace(re, idx + ' ' + val);
        } else {
          str += '\n' + idx + ' ' + val;
        }
      }
    }
  } else {
    str = IniFile.iniToString(ini);
  }

  if (str) {
    if (!file.open("w")) {
      throw "Unable to open ini file " + file + ": " + file.error;
    }
    file.write(str);
    file.close();
  }

  return ini;
};

// By default, I update ini files instead of overwriting them.
IniFile.write = IniFile.update;


// convert an object into an easy-to-read string
listProps = function(obj) {
  var s = '';
  for (var x in obj) {
    s += x + ":\t";
    try {
      var o = obj[x];
      s += (typeof o == "function") ? "[function]" : o;
    } catch (e) {
    }
    s += "\r\n";
  }
  return s;
};


// a simple demo of the INI functions
IniFile.demo1 = function() {
  var obj  = {
    name: "bob",
    age: 24
  };
 
  alert(listProps(obj));

  IniFile.write("~/testfile.ini", obj);

  var z = IniFile.read("~/testfile.ini", obj);

  alert(listProps(z));
};

// a simple demo of the INI functions
IniFile.demo2 = function() {
  var f = new File("~/testfile.ini");

  var obj = {};
  obj.city = "singapore";

  IniFile.read(f, obj);
  var z = IniFile.write(f, obj);

  alert(listProps(z));
};

// IniFile.demo1();
// IniFile.demo2();

"IniFile.jsx";

// EOF
Patrick

IniFile

Post by Patrick »

Thanks a lot for posting this, I have been wanting to use them for awhile but have been procrastinating on learning how.

Patrick
v.bampton

IniFile

Post by v.bampton »

Thanks X. It definitely works on Mac and PC, CS and CS2, so I'm off to study it in detail now!!
v.bampton

IniFile

Post by v.bampton »

When I tried to use the Overwrite instead of Update option, I came up with errors on line 115 and 125, missing a couple of functions.

It seems to be fixed by the following, but I haven't studied it in great detail yet - is this right?

It's changed
var file = IniFile.convertFptr.iniFileToFile(iniFile);
to
var file = IniFile.convertFptr(iniFile);
as it didn't recognise iniFileToFile

and
var str = IniString.iniToString(ini);
to
var str = IniFile.iniToString(ini);
as it was missing IniString.

I've just borrowed bits from IniFile.Update, and it works on CS ok - or have I missed the point completely?

Code: Select all//
// Write an object to an ini file overwriting whatever was there before
//
IniFile.overwrite = function(iniFile, ini) {
  if (!ini || !iniFile) {
    return;
  }
  var file = IniFile.convertFptr(iniFile);

  if (!file) {
    throw "Bad ini file specified: \"" + iniFile + "\".";
  }
 
  if (!file.open("w")) {
    throw "Unable to open ini file " + file + ": " + file.error;
  }

  var str = IniFile.iniToString(ini);
  file.write(str);
  file.close();

  return ini;
};
IniFile.trim = function(value) {
   return value.replace(/^[\s]+|[\s]+$/g, '');
};
xbytor

IniFile

Post by xbytor »

Your changes are correct. I extracted this code from another script, renamed and reorganized it, but didn't test 'overwrite' in the process. Thanks for finding these. I'll make the changes to my script.

-X
v.bampton

IniFile

Post by v.bampton »

Wow! You mean I got it right!! Excellent!

Thanks for the script X, it's working great!
v.bampton

IniFile

Post by v.bampton »

X, how you do get it to remember checkbox values?

It's working brilliantly on text fields, but it marks all checkboxes as true - any ideas?
xbytor

IniFile

Post by xbytor »

You have to remember that all values are strings. If you need to use them as booleans or numbers, you have to convert them. These are the two functions that I use for this purpose:

Code: Select allfunction toBoolean(s) {
  if (s == undefined) return false;
  if (s.constructor == Boolean) return s.valueOf();
  if (s.constructor == String)  return s.toLowerCase() == "true";
  return Boolean(s);
};

function toNumber(s) {
  if (s == undefined) return NaN;
  if (s.constructor == Number) return s.valueOf();
  return Number(s.toString());
};



If you are trying to set a checkbox value like this:

Code: Select allchk.value = ini.chk;

it will be true if you have "true" or "false" in the ini file. You have to convert it like this:

Code: Select allchk.value = toBoolean(ini.chk);

In scripts where I have a lot of ini settings, I typically have a function called 'rationalize'. This function takes the ini object that I've just read in from the ini file and does all of the conversions to numbers, booleans, Files, Colors, Fonts, or whatever. If I don't do it like this, I'll eventually forget to do the conversion in my code somewhere and I'll wonder when the checkbox is always true.

If you want to take a look at a script that uses these techniques to the extreme, take a look at CSX. Much of what's in IniFile was built in response to the needs of that script.

-X
Mike Hale

IniFile

Post by Mike Hale »

Here is the lite version

Code: Select allvar b = false
var bStr = b.toString()
!!bStr.match('true')
v.bampton

IniFile

Post by v.bampton »

That makes much more sense - thanks for explaining WHY it was happening, as well as how to solve it!