docs: function initCustomPointerTypemaps(typemapsdir) {
    return fs.readdirAsync(typemapsdir).then(function(dirs) {
      return Promise.all(_.map(dirs, function(dir) {
        // get all js*.i directives from class-specific subdirectories, to be parsed below for %typemaps directives
        return fs.readdirAsync(typemapsdir + '/' + dir).then(function(files) {
          var directive = _.find(files, function(fn) {
            return ((path.extname(fn) == '.i') && (path.basename(fn).search(/^js/) != -1));
          });
          var data = {};
          if (directive) {
            data[dir] = typemapsdir + '/' + dir + '/' + directive;
          }
          return data;
        }).catch(function(e) {
          // get all .i directives from top level directory, and parse for %array_class directives
          if (e.code == 'ENOTDIR') {
            var fn = dir;
            if (path.extname(fn) == '.i') {
              return fs.readFileAsync(typemapsdir + '/' + fn, 'utf8').then(function(directives) {
                var arraytypes = _.filter(directives.split(/\n/), function(line) {
                  return (line.search(/^%array_class/) != -1);
                });
                _.each(arraytypes, function(arraytype) {
                  var parsed = arraytype.match(/%array_class\(([A-Za-z0-9_]+)[\s]*,[\s]*([A-Za-z0-9_]+)\)/);
                  if (parsed) {
                    var from = parsed[1];
                    var to = parsed[2];
                    xml2js.ARRAY_TYPEMAPS[from] = { arrayType: to, classes: [] };
                  } else {
                    console.log('Incorrectly parsed array_class from ' + fn + ': ' + arraytype);
                  }
                });
              });
            }
          } else {
            throw e;
          }
        });
      }));
    }).then(function(__directivesFiles) {
      // parse for %typemaps & %pointer_functions directives
      var _directivesFiles = _.filter(__directivesFiles, function(data) { return !_.isEmpty(data); });
      var directivesFiles = _.object(_.map(_directivesFiles, _.keys), _.flatten(_.map(_directivesFiles, _.values)));
      return Promise.all(_.map(directivesFiles, function(directivesFn, className) {
        return fs.readFileAsync(directivesFn, 'utf8').then(function(directives) {
          var typemaps = _.filter(directives.split(/\n/), function(line) {
            return (line.search(/^%typemap/) != -1);
          });
          _.each(typemaps, function(typemap) {
            var parsed = typemap.match(/%typemap\((in|out)\)[\s]+([A-Za-z0-9_]+[\s]*[\*])/);
            if (parsed) {
              var dir = parsed[1]; // TODO: ignored for now
              var type = normalizePointer(parsed[2]);
              var datatype = getPointerDataType(type);
              if (_.has(xml2js.ARRAY_TYPEMAPS, datatype)) {
                xml2js.ARRAY_TYPEMAPS[datatype].classes.push(className);
              } else {
                console.log('Ignored typemap from ' + directivesFn + ': ' + typemap.replace('{', '') + ' (no %array_class directive found for ' + datatype + ')');
              }
            } else {
              console.log('Ignored typemap from ' + directivesFn + ': ' + typemap.replace('{', '') + ' (only considering in/out typemaps of pointer types)');
            }
          });
          var ptrfns = _.filter(directives.split(/\n/), function(line) {
            return (line.search(/^%pointer_functions/) != -1);
          });
          _.each(ptrfns, function(ptrfn) {
            var parsed = ptrfn.match(/%pointer_functions\(([A-Za-z0-9_]+)[\s]*,[\s]*([A-Za-z0-9_]+)\)/);
            if (parsed) {
              var from = parsed[1];
              var to = parsed[2];
              if (!_.has(xml2js.POINTER_TYPEMAPS, className)) {
                xml2js.POINTER_TYPEMAPS[className] = {};
              }
              xml2js.POINTER_TYPEMAPS[className][from] = to;
            }
          });
        });
      }));
    });
  }