OSSP CVS Repository

ossp - Check-in [4903]
Not logged in
[Honeypot]  [Browse]  [Home]  [Login]  [Reports
[Search]  [Ticket]  [Timeline
  [Patchset]  [Tagging/Branching

Check-in Number: 4903
Date: 2004-Dec-19 20:36:25 (local)
2004-Dec-19 19:36:25 (UTC)
User:rse
Branch:
Comment: Fixed cfg_node_get() function after recent introduction of LOAN/GIFT/COPY argument passing semantics. This inbreaks the Perl API's unpack() function.
Tickets:
Inspections:
Files:
ossp-pkg/cfg/ChangeLog      1.28 -> 1.29     8 inserted, 1 deleted
ossp-pkg/cfg/README      1.13 -> 1.14     1 inserted, 1 deleted
ossp-pkg/cfg/cfg_node.c      1.23 -> 1.24     1 inserted, 1 deleted
ossp-pkg/cfg/cfg_vers.c      1.11 -> 1.12     9 inserted, 9 deleted
ossp-pkg/cfg/perl/cfg.pm      added-> 1.11

ossp-pkg/cfg/ChangeLog 1.28 -> 1.29

--- ChangeLog    2004/12/04 12:48:40     1.28
+++ ChangeLog    2004/12/19 19:36:25     1.29
@@ -8,7 +8,14 @@
 
   CHANGELOG
 
- Changes between 0.9.5 and 0.9.6 (27-Nov-2004 to xx-Dec-2004):
+ Changes between 0.9.6 and 0.9.7 (04-Dec-2004 to 19-Dec-2004):
+
+   *) Fixed cfg_node_get() function after recent introduction
+      of LOAN/GIFT/COPY  argument passing semantics. This
+      inbreaks the Perl API's unpack() function.
+      [Ralf S. Engelschall <rse@engelschall.com>]
+
+ Changes between 0.9.5 and 0.9.6 (27-Nov-2004 to 04-Dec-2004):
 
    *) Plug remaining memory leaks by introducing the usual
       LOAN/GIFT/COPY argument passing semantics to cfg_node_{set,get}


ossp-pkg/cfg/README 1.13 -> 1.14

--- README       2004/12/04 12:54:41     1.13
+++ README       2004/12/19 19:36:25     1.14
@@ -5,7 +5,7 @@
   |_|_|_|  \___/|____/____/|_|      \___|_|  \__, |
                                              |___/
   OSSP cfg - Configuration Parsing
-  Version 0.9.6 (04-Dec-2004)
+  Version 0.9.7 (19-Dec-2004)
 
   ABSTRACT
 


ossp-pkg/cfg/cfg_node.c 1.23 -> 1.24

--- cfg_node.c   2004/12/04 12:48:40     1.23
+++ cfg_node.c   2004/12/19 19:36:25     1.24
@@ -253,7 +253,7 @@
 
     /* dispatch into individual attribute handling */
     va_start(ap, attr);
-    switch (attr) {
+    switch (attr & ~(CFG_ATTR_LOAN|CFG_ATTR_GIFT|CFG_ATTR_COPY)) {
         case CFG_NODE_ATTR_PARENT: {
             cfg_node_t **n = (cfg_node_t **)va_arg(ap, void *);
             if (n == NULL)


ossp-pkg/cfg/cfg_vers.c 1.11 -> 1.12

--- cfg_vers.c   2004/12/04 12:54:41     1.11
+++ cfg_vers.c   2004/12/19 19:36:25     1.12
@@ -8,7 +8,7 @@
 #ifndef _CFG_VERS_C_
 #define _CFG_VERS_C_
 
-#define __CFG_VERSION 0x009206
+#define __CFG_VERSION 0x009207
 
 typedef struct {
     const int   v_hex;
@@ -32,14 +32,14 @@
 #undef  _CFG_VERS_C_AS_HEADER_
 
 __cfg_version_t __cfg_version = {
-    0x009206,
-    "0.9.6",
-    "0.9.6 (04-Dec-2004)",
-    "This is OSSP cfg, Version 0.9.6 (04-Dec-2004)",
-    "OSSP cfg 0.9.6 (04-Dec-2004)",
-    "OSSP cfg/0.9.6",
-    "@(#)OSSP cfg 0.9.6 (04-Dec-2004)",
-    "$Id: cfg_vers.c,v 1.11 2004/12/04 12:54:41 rse Exp $"
+    0x009207,
+    "0.9.7",
+    "0.9.7 (19-Dec-2004)",
+    "This is OSSP cfg, Version 0.9.7 (19-Dec-2004)",
+    "OSSP cfg 0.9.7 (19-Dec-2004)",
+    "OSSP cfg/0.9.7",
+    "@(#)OSSP cfg 0.9.7 (19-Dec-2004)",
+    "$Id: cfg_vers.c,v 1.12 2004/12/19 19:36:25 rse Exp $"
 };
 
 #endif /* _CFG_VERS_C_AS_HEADER_ */


ossp-pkg/cfg/perl/cfg.pm -> 1.11

*** /dev/null    Sun Apr 28 07:33:00 2024
--- -    Sun Apr 28 07:34:30 2024
***************
*** 0 ****
--- 1,635 ----
+ ##
+ ##  OSSP cfg - Configuration Parsing
+ ##  Copyright (c) 2002-2004 Ralf S. Engelschall <rse@engelschall.com>
+ ##  Copyright (c) 2002-2004 The OSSP Project <http://www.ossp.org/>
+ ##  Copyright (c) 2002-2004 Cable & Wireless <http://www.cw.com/>
+ ##
+ ##  This file is part of OSSP cfg, a configuration parsing library which
+ ##  can be found at http://www.ossp.org/pkg/lib/cfg/.
+ ##
+ ##  Permission to use, copy, modify, and distribute this software for
+ ##  any purpose with or without fee is hereby granted, provided that
+ ##  the above copyright notice and this permission notice appear in all
+ ##  copies.
+ ##
+ ##  THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
+ ##  WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ ##  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.
+ ##  IN NO EVENT SHALL THE AUTHORS AND COPYRIGHT HOLDERS AND THEIR
+ ##  CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
+ ##  SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
+ ##  LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
+ ##  USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
+ ##  ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
+ ##  OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
+ ##  OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ ##  SUCH DAMAGE.
+ ##
+ ##  cfg.pm: Perl Binding (Perl part)
+ ##
+ 
+ package OSSP::cfg;
+ 
+ use 5.008;
+ use strict;
+ use warnings;
+ use Carp;
+ use XSLoader;
+ use Exporter;
+ 
+ ##
+ ##  API Definition
+ ##
+ 
+ #   API version
+ our $VERSION = do { my @v = ('0.9.7' =~ m/\d+/g); sprintf("%d.".("%02d"x$#v), @v); };
+ 
+ #   API inheritance
+ our @ISA = qw(Exporter);
+ 
+ #   API symbols
+ my $symbols = {
+     'const' => [qw(
+         CFG_OK
+         CFG_ERR_ARG
+         CFG_ERR_USE
+         CFG_ERR_MEM
+         CFG_ERR_SYS
+         CFG_ERR_FMT
+         CFG_ERR_INT
+         CFG_ERR_SYN
+         CFG_ERR_NDE
+         CFG_FMT_CFG
+         CFG_FMT_XML
+         CFG_NODE_TYPE_SEQ
+         CFG_NODE_TYPE_DIR
+         CFG_NODE_TYPE_OPT
+         CFG_NODE_TYPE_ARG
+         CFG_NODE_ATTR_PARENT
+         CFG_NODE_ATTR_LBROTH
+         CFG_NODE_ATTR_RBROTH
+         CFG_NODE_ATTR_CHILD1
+         CFG_NODE_ATTR_CHILDL
+         CFG_NODE_ATTR_CHILDS
+         CFG_NODE_ATTR_NODES
+         CFG_NODE_ATTR_DEPTH
+         CFG_NODE_ATTR_SRCNAME
+         CFG_NODE_ATTR_SRCPOS
+         CFG_NODE_ATTR_TYPE
+         CFG_NODE_ATTR_TOKEN
+         CFG_NODE_ATTR_DATA
+         CFG_DATA_TYPE_PTR
+         CFG_DATA_TYPE_STR
+         CFG_DATA_TYPE_INT
+         CFG_DATA_TYPE_FLT
+         CFG_DATA_CTRL_CLONE
+         CFG_DATA_CTRL_DESTROY
+         CFG_DATA_ATTR_TYPE
+         CFG_DATA_ATTR_VALUE
+         CFG_DATA_ATTR_CTRL
+     )],
+     'func' => [qw(
+         cfg_create
+         cfg_destroy
+         cfg_error
+         cfg_version
+         cfg_import
+         cfg_export
+         cfg_node_create
+         cfg_node_destroy
+         cfg_node_clone
+         cfg_node_set
+         cfg_node_get
+         cfg_node_root
+         cfg_node_select
+         cfg_node_find
+         cfg_node_apply
+         cfg_node_cmp
+         cfg_node_link
+         cfg_node_unlink
+         cfg_data_set
+         cfg_data_get
+         cfg_data_ctrl
+     )]
+ };
+ 
+ #   API symbol exportation
+ our %EXPORT_TAGS = (
+     'all'   => [ @{$symbols->{'const'}}, @{$symbols->{'func'}} ],
+     'const' => [ @{$symbols->{'const'}} ],
+     'func'  => [ @{$symbols->{'func'}}  ]
+ );
+ our @EXPORT_OK = @{$EXPORT_TAGS{'all'}};
+ our @EXPORT    = ();
+ 
+ ##
+ ##  High-Level Perl Module OO-style API
+ ##  (just an OO wrapper around the C-style API)
+ ##
+ 
+ sub new {
+     my $proto = shift;
+     my $class = ref($proto) || $proto;
+     my $self = {};
+     bless ($self, $class);
+     $self->{-cfg} = undef;
+     $self->{-rc}  = $self->CFG_OK;
+     my $rc = cfg_create($self->{-cfg});
+     if ($rc != $self->CFG_OK) {
+         croak(sprintf("OSSP::cfg::new: cfg_create: %s (%d)", $self->error($rc), $rc));
+     }
+     return $self;
+ }
+ 
+ sub DESTROY ($) {
+     my ($self) = @_;
+     $self->{-rc} = cfg_destroy($self->{-cfg}) if (defined($self->{-cfg}));
+     if ($self->{-rc} != $self->CFG_OK) {
+         carp(sprintf("OSSP::cfg::DESTROY: cfg_destroy: %s (%d)", $self->error($self->{-rc}), $self->{-rc}));
+         return;
+     }
+     $self->{-cfg} = undef;
+     $self->{-rc}  = undef;
+     return;
+ }
+ 
+ sub import { # ($$$$)
+     #   ATTENTION: The OSSP cfg API function "import" conflicts with
+     #   the standardized "import" method the Perl world expects from
+     #   their modules. In order to keep the Perl binding consist
+     #   with the C API, we solve the conflict under run-time by
+     #   distinguishing between the two types of "import" calls.
+     if (defined($_[0]) and ref($_[0]) =~ m/^OSSP::cfg/) {
+         #   the regular OSSP::cfg "import" method
+         my ($self, $node, $fmt, $in) = @_;
+         $self->{-rc} = cfg_import($self->{-cfg}, $node, $fmt, $in, length($in));
+         return ($self->{-rc} == $self->CFG_OK);
+     }
+     else {
+         #   the special Perl "import" method
+         #   (usually inherited from the Exporter)
+         no strict "refs";
+         return OSSP::cfg->export_to_level(1, @_);
+     }
+ }
+ 
+ sub export { # ($$$)
+     #   ATTENTION: The OSSP cfg API function "export" conflicts with
+     #   the standardized "export" method the Perl world expects from
+     #   their modules. In order to keep the Perl binding consist
+     #   with the C API, we solve the conflict under run-time by
+     #   distinguishing between the two types of "export" calls.
+     if (defined($_[0]) and ref($_[0]) =~ m/^OSSP::cfg/) {
+         #   the regular OSSP::cfg "export" method
+         my ($self, $node, $fmt) = @_;
+         my $out;
+         $self->{-rc} = cfg_export($self->{-cfg}, $node, $fmt, $out, 0);
+         return ($self->{-rc} == $self->CFG_OK ? $out : undef);
+     }
+     else {
+         #   the special Perl "export" method
+         #   (usually inherited from the Exporter)
+         return Exporter::export(@_);
+     }
+ }
+ 
+ sub error ($;$) {
+     my ($self, $rc) = @_;
+     $rc = $self->{-rc} if (not defined($rc));
+     my $error;
+     if (cfg_error($self->{-cfg}, $rc, $error) != $self->CFG_OK) {
+         croak("OSSP::cfg::error: cfg_error: INTERNAL ERROR");
+     }
+     return wantarray ? ($error, $rc) : $error;
+ }
+ 
+ sub version () {
+     my ($self) = @_;
+     return cfg_version();
+ }
+ 
+ sub node_create ($) {
+     my ($self) = @_;
+     my $node;
+     $self->{-rc} = cfg_node_create($self->{-cfg}, $node);
+     return ($self->{-rc} == $self->CFG_OK ? $node : undef);
+ }
+ 
+ sub node_destroy ($$) {
+     my ($self, $node) = @_;
+     $self->{-rc} = cfg_node_destroy($self->{-cfg}, $node);
+     return ($self->{-rc} == $self->CFG_OK);
+ }
+ 
+ sub node_clone ($$) {
+     my ($self, $node) = @_;
+     my $node2;
+     $self->{-rc} = cfg_node_clone($self->{-cfg}, $node, $node2);
+     return ($self->{-rc} == $self->CFG_OK ? $node2 : undef);
+ }
+ 
+ sub node_set ($$$$) {
+     my ($self, $node, $attr, $arg) = @_;
+     $self->{-rc} = cfg_node_set($self->{-cfg}, $node, $attr, $arg);
+     return ($self->{-rc} == $self->CFG_OK);
+ }
+ 
+ sub node_get ($$$) {
+     my ($self, $node, $attr) = @_;
+     my $arg;
+     $self->{-rc} = cfg_node_get($self->{-cfg}, $node, $attr, $arg);
+     return ($self->{-rc} == $self->CFG_OK ? $arg : undef);
+ }
+ 
+ sub node_root ($;$) {
+     my ($self, $node_new) = @_;
+     $node_new ||= \0;
+     my $node_old;
+     $self->{-rc} = cfg_node_root($self->{-cfg}, $node_new, $node_old);
+     return ($self->{-rc} == $self->CFG_OK ? $node_old : undef);
+ }
+ 
+ sub node_select ($$$) {
+     my ($self, $node, $spec) = @_;
+     my $result;
+     $self->{-rc} = cfg_node_select($self->{-cfg}, $node, $result, $spec);
+     return ($self->{-rc} == $self->CFG_OK ? $result : undef);
+ }
+ 
+ sub node_find ($$$$) {
+     my ($self, $node, $cb_fct_cmp, $cb_ctx_cmp) = @_;
+     my $cont;
+     $self->{-rc} = cfg_node_find($self->{-cfg}, $node, $cb_fct_cmp, $cb_ctx_cmp, $cont);
+     return ($self->{-rc} == $self->CFG_OK ? $cont : undef);
+ }
+ 
+ sub node_apply ($$$$) {
+     my ($self, $node, $cb_fct_cmp, $cb_ctx_cmp, $cb_fct_cb, $cb_ctx_cb) = @_;
+     $self->{-rc} = cfg_node_apply($self->{-cfg}, $node, $cb_fct_cmp, $cb_ctx_cmp, $cb_fct_cb, $cb_ctx_cb);
+     return ($self->{-rc} == $self->CFG_OK);
+ }
+ 
+ sub node_cmp ($$$) {
+     my ($self, $node, $token) = @_;
+     $self->{-rc} = cfg_node_cmp($self->{-cfg}, $node, $token);
+     return ($self->{-rc} == $self->CFG_OK);
+ }
+ 
+ sub node_link ($$$$) {
+     my ($self, $node, $id, $node2) = @_;
+     $self->{-rc} = cfg_node_link($self->{-cfg}, $node, $id, $node2);
+     return ($self->{-rc} == $self->CFG_OK);
+ }
+ 
+ sub node_unlink ($$) {
+     my ($self, $node) = @_;
+     $self->{-rc} = cfg_node_unlink($self->{-cfg}, $node);
+     return ($self->{-rc} == $self->CFG_OK);
+ }
+ 
+ sub data_set ($$$$) {
+     my ($self, $data, $attr, $value) = @_;
+     $self->{-rc} = cfg_data_set($data, $attr, $value);
+     return ($self->{-rc} == $self->CFG_OK);
+ }
+ 
+ sub data_get ($$$) {
+     my ($self, $data, $attr) = @_;
+     my $value;
+     $self->{-rc} = cfg_data_get($data, $attr, $value);
+     return ($self->{-rc} == $self->CFG_OK ? $value : undef);
+ }
+ 
+ sub data_ctrl ($$$;$) {
+     my ($self, $data, $ctrl, $value) = @_;
+     $self->{-rc} = cfg_data_ctrl($data, $ctrl, $value);
+     return ($self->{-rc} == $self->CFG_OK);
+ }
+ 
+ ##
+ ##  Low-Level Perl XS C-style API
+ ##  (actually just the activation of the XS part)
+ ##
+ 
+ #   auto-loading constants
+ sub AUTOLOAD {
+     my $constname;
+     our $AUTOLOAD;
+     ($constname = $AUTOLOAD) =~ s/.*:://;
+     croak "&OSSP::cfg::constant not defined" if ($constname eq 'constant');
+     my ($error, $val) = constant($constname);
+     croak $error if ($error);
+     { no strict 'refs'; *$AUTOLOAD = sub { $val }; }
+     goto &$AUTOLOAD;
+ }
+ 
+ #   static-loading functions
+ XSLoader::load('OSSP::cfg', $VERSION);
+ 
+ ##
+ ##  Highest-Level Perl Convenience API
+ ##  (based on OSSP::cfg Perl API above)
+ ##
+ 
+ package OSSP::cfg::simple;
+ 
+ use 5.008;
+ use strict;
+ use warnings;
+ use Carp;
+ use IO::File;
+ 
+ #   object constructor
+ sub new ($;$) {
+     my ($proto, $input) = @_;
+     my $class = ref($proto) || $proto;
+     my $self = {};
+     bless ($self, $class);
+     $self->{-cfg} = new OSSP::cfg
+         or croak "failed to create OSSP::cfg object";
+     $self->parse($input) if (defined($input));
+     return $self;
+ }
+ 
+ #   object destructor
+ sub DESTROY ($) {
+     my ($self) = @_;
+     undef $self->{-cfg};
+     return;
+ }
+ 
+ #   parse textual syntax into node tree
+ sub parse ($$) {
+     my ($self, $input) = @_;
+     my $cfg = $self->{-cfg};
+     if (length($input) < 1024 and $input !~ m|[{}&\n\s]|s) {
+         if (-f $input) {
+             my $io = new IO::File "<" . $input
+                or croak "unable to read from configuration file \"".$input."\"";
+             { local $/; $input = <$io>; }
+             $io->close();
+         }
+     }
+     $cfg->import(\0, $cfg->CFG_FMT_CFG, $input)
+         or die "failed to parse configuration: " . $cfg->error();
+     return $self;
+ }
+ 
+ #   format textual syntax from node tree
+ sub format ($$) {
+     my ($self) = @_;
+     my $cfg = $self->{-cfg};
+     my $root = $cfg->node_root();
+     my $str = $cfg->export($root, $cfg->CFG_FMT_CFG)
+         or die "failed to format configuration: " . $cfg->error();
+     return $str;
+ }
+ 
+ #   pack Perl complex data structure into node tree
+ sub pack ($$) {
+     my ($self, $tree) = @_;
+ 
+     #   destroy old and create new OSSP::cfg object
+     undef $self->{-cfg};
+     my $cfg = new OSSP::cfg
+         or croak "failed to create OSSP::cfg object";
+     $self->{-cfg} = $cfg;
+ 
+     #   start parsing with a sequence
+     my $node = &parse_seq($cfg, $tree);
+ 
+     #   parse a sequence (reference) scalar
+     sub parse_seq {
+         my ($cfg, $seq) = @_;
+         my $node = undef;
+         if (ref($seq) eq 'ARRAY') {
+             $node = $cfg->node_create();
+             $cfg->node_set($node, $cfg->CFG_NODE_ATTR_TYPE, $cfg->CFG_NODE_TYPE_SEQ);
+             if (@{$seq} > 0) {
+                 my $child = &parse_dir($cfg, $seq->[0]);
+                 $cfg->node_set($node, $cfg->CFG_NODE_ATTR_CHILD1, $child);
+                 my $sibling = $child;
+                 foreach my $dir (@{$seq}[1..$#{$seq}]) {
+                     $child = &parse_dir($cfg, $dir);
+                     $cfg->node_set($sibling, $cfg->CFG_NODE_ATTR_RBROTH, $child);
+                     $sibling = $child;
+                 }
+             }
+         }
+         return $node;
+     }
+ 
+     #   parse a directive (reference) scalar
+     sub parse_dir {
+         my ($cfg, $dir) = @_;
+         my $node = undef;
+         if (ref($dir) eq 'ARRAY') {
+             $node = $cfg->node_create();
+             $cfg->node_set($node, $cfg->CFG_NODE_ATTR_TYPE, $cfg->CFG_NODE_TYPE_DIR);
+             if (@{$dir} > 0) {
+                 my $child = &parse_tok($cfg, $dir->[0]);
+                 $cfg->node_set($node, $cfg->CFG_NODE_ATTR_CHILD1, $child);
+                 my $sibling = $child;
+                 foreach my $tok (@{$dir}[1..$#{$dir}]) {
+                     $child = &parse_tok($cfg, $tok);
+                     $cfg->node_set($sibling, $cfg->CFG_NODE_ATTR_RBROTH, $child);
+                     $sibling = $child;
+                 }
+             }
+         }
+         return $node;
+     }
+ 
+     #   parse a token scalar
+     sub parse_tok {
+         my ($cfg, $tok) = @_;
+         my $node;
+         if (ref($tok) eq 'ARRAY') {
+             #   token is actually a sub-sequence
+             $node = &parse_seq($cfg, $tok); # recursion
+         }
+         else {
+             #   token is a plain one
+             $node = $cfg->node_create();
+             $cfg->node_set($node, $cfg->CFG_NODE_ATTR_TYPE, $cfg->CFG_NODE_TYPE_ARG);
+             $cfg->node_set($node, $cfg->CFG_NODE_ATTR_TOKEN, $tok);
+         }
+         return $node;
+     }
+ 
+     #   set generated node tree into OSSP::cfg object
+     $cfg->node_root($node);
+ 
+     return $self;
+ }
+ 
+ #   unpack Perl complex data structure from node tree
+ sub unpack ($;%) {
+     my ($self, %arg) = @_;
+     my $cfg = $self->{-cfg};
+ 
+     #   start traversing the node tree from the root node
+     my $root = $cfg->node_root();
+     my $tree = &traverse($cfg, \%arg, $root);
+ 
+     #   node processing function
+     sub traverse {
+         my ($cfg, $arg, $node) = @_;
+         return if (not(ref($node) and $$node != 0));
+         my $type = $cfg->node_get($node, $cfg->CFG_NODE_ATTR_TYPE);
+         if ($type == $cfg->CFG_NODE_TYPE_SEQ) {
+             #   node is a sequence
+             my $seq = [];
+             $node = $cfg->node_get($node, $cfg->CFG_NODE_ATTR_CHILD1);
+             while (ref($node) and $$node != 0) {
+                 my $tree = &traverse($cfg, $arg, $node); # recursion
+                 push(@{$seq}, $tree) if (defined($tree));
+                 $node = $cfg->node_get($node, $cfg->CFG_NODE_ATTR_RBROTH);
+             }
+             #   optional post-processing of data structure
+             $seq = &unpack_postproc_merge   ($seq, $arg->{-merge})   if (defined($arg->{-merge}));
+             $seq = &unpack_postproc_index   ($seq, $arg->{-index})   if (defined($arg->{-index}));
+             $seq = &unpack_postproc_strip   ($seq, $arg->{-strip})   if (defined($arg->{-strip}));
+             $seq = &unpack_postproc_flatten ($seq, $arg->{-flatten}) if (defined($arg->{-flatten}));
+             return $seq;
+         }
+         elsif ($type == $cfg->CFG_NODE_TYPE_DIR) {
+             #   node is a directive
+             my $dir = [];
+             $node = $cfg->node_get($node, $cfg->CFG_NODE_ATTR_CHILD1);
+             while (ref($node) and $$node != 0) {
+                 my $tree = &traverse($cfg, $arg, $node); # recursion
+                 push(@{$dir}, $tree) if (defined($tree));
+                 $node = $cfg->node_get($node, $cfg->CFG_NODE_ATTR_RBROTH);
+             }
+             return $dir;
+         }
+         else {
+             #   node is a token
+             my $token = $cfg->node_get($node, $cfg->CFG_NODE_ATTR_TOKEN);
+             return $token;
+         }
+     }
+     return $tree;
+ }
+ 
+ #   unpack helper function:
+ #   optionally merge arguments of DIRectives starting with same TOKen.
+ #   complexity: O(n) (actually 3*n)
+ sub unpack_postproc_merge {
+     my ($seq, $re) = @_;
+     
+     #   pass 1: determine directive names and their 
+     #   occurences in sequence. complexity: O(n)
+     my $dir = {};
+     for (my $i = 0; $i < @{$seq}; $i++) {
+         if (    ref($seq->[$i]) eq 'ARRAY' 
+             and not ref($seq->[$i]->[0])
+             and $seq->[$i]->[0] =~ m/^$re$/s) {
+             my $name = $seq->[$i]->[0];
+             $dir->{$name} = [] if (not defined($dir->{$name}));
+             push(@{$dir->{$name}}, $i);
+         }
+     }
+ 
+     #   pass 2: append arguments of second and following sequences
+     #   to arguments of first directive. complexity: O(n)
+     foreach my $name (keys(%{$dir})) {
+         if (@{$dir->{$name}} > 1) {
+             my $f = $dir->{$name}->[0];
+             foreach my $n (@{$dir->{$name}}[1..$#{$dir->{$name}}]) {
+                 foreach my $tok (@{$seq->[$n]}[1..$#{$seq->[$n]}]) {
+                     push(@{$seq->[$f]}, $tok);
+                 }
+                 $seq->[$n] = undef;
+             }
+         }
+     }
+ 
+     #   pass 3: shrink sequence by eliminating removing entries
+     #   complexity: O(n)
+     for (my $i = 0; $i < @{$seq}; $i++) {
+         if (not defined($seq->[$i])) {
+             splice(@{$seq}, $i, 1);
+             $i--; # compensate for splice operation
+         }
+     }
+ 
+     return $seq;
+ }
+ 
+ #   unpack helper function:
+ #   optionally annotate SEQuence nodes with a DIRective name indexing hash
+ #   complexity: O(n)
+ sub unpack_postproc_index {
+     my ($seq, $re) = @_;
+ 
+     #   create an index hash
+     my $idx = {};
+     for (my $i = 0; $i < @{$seq}; $i++) {
+         if (ref($seq->[$i]) eq 'ARRAY') {
+             #   determine name of directive, i.e.,
+             #   first non-sub-sequence element
+             my $name = {};
+             for (my $j = 0; $j < @{$seq->[$j]}; $j++) {
+                 $name = $seq->[$i]->[$j];
+                 last if (not ref($name));
+             }
+             if (    not ref($name)
+                 and $name =~ m/^$re$/s 
+                 and not defined($idx->{$name})) {
+                 #    remember position of directive's
+                 #    first occurrence in sequence
+                 $idx->{$name} = ($i + 1);
+             }
+         }
+     }
+ 
+     #   insert index at first position of sequence array following
+     #   perlref(1)'s "Pseudo-hashes: Using an array as a hash" approach.
+     unshift(@{$seq}, $idx);
+ 
+     return $seq;
+ }
+ 
+ #   unpack helper function:
+ #   strip directive command/start token
+ #   complexity: O(n)
+ sub unpack_postproc_strip {
+     my ($seq, $re) = @_;
+ 
+     foreach my $dir (@{$seq}) {
+         if (    ref($dir) eq 'ARRAY'
+             and $dir->[0] =~ m/^$re$/s) {
+             #   remove first token of directive array
+             shift(@{$dir});
+         }
+     }
+ 
+     return $seq;
+ }
+ 
+ #   unpack helper function:
+ #   flatten empty and single token directives
+ sub unpack_postproc_flatten {
+     my ($seq, $empty) = @_;
+ 
+     for (my $i = 0; $i < @{$seq}; $i++) {
+         if (ref($seq->[$i]) eq 'ARRAY') {
+             if (@{$seq->[$i]} == 0) {
+                 #   replace empty directive array
+                 #   (can exist after "dirstrip" processing!)
+                 $seq->[$i] = $empty;
+             }
+             elsif (@{$seq->[$i]} == 1) {
+                 #   simplify single token directive array
+                 $seq->[$i] = $seq->[$i]->[0];
+             }
+         }
+     }
+ 
+     return $seq;
+ }
+ 
+ 1;
+ 

CVSTrac 2.0.1