This file is indexed.

/usr/share/perl5/Graph/Easy/Layout/Chain.pm is in libgraph-easy-perl 0.75-2.

This file is owned by root:root, with mode 0o644.

The actual contents of the file can be viewed below.

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
#############################################################################
# One chain of nodes in a Graph::Easy - used internally for layouts.
#
# (c) by Tels 2004-2006. Part of Graph::Easy
#############################################################################

package Graph::Easy::Layout::Chain;

use Graph::Easy::Base;
$VERSION = '0.75';
@ISA = qw/Graph::Easy::Base/;

use strict;
use warnings;

use Graph::Easy::Util qw(ord_values);

use constant {
  _ACTION_NODE  => 0, # place node somewhere
  _ACTION_TRACE => 1, # trace path from src to dest
  _ACTION_CHAIN => 2, # place node in chain (with parent)
  _ACTION_EDGES => 3, # trace all edges (shortes connect. first)
  };

#############################################################################

sub _init
  {
  # Generic init routine, to be overriden in subclasses.
  my ($self,$args) = @_;
  
  foreach my $k (sort keys %$args)
    {
    if ($k !~ /^(start|graph)\z/)
      {
      require Carp;
      Carp::confess ("Invalid argument '$k' passed to __PACKAGE__->new()");
      }
    $self->{$k} = $args->{$k};
    }
 
  $self->{end} = $self->{start};
 
  # store chain at node (to lookup node => chain info)
  $self->{start}->{_chain} = $self;
  $self->{start}->{_next} = undef;

  $self->{len} = 1;

  $self;
  }

sub start
  {
  # return first node in the chain
  my $self = shift;

  $self->{start};
  }

sub end
  {
  # return last node in the chain
  my $self = shift;

  $self->{end};
  }

sub add_node
  {
  # add a node at the end of the chain
  my ($self, $node) = @_;

  # store at end
  $self->{end}->{_next} = $node;
  $self->{end} = $node;

  # store chain at node (to lookup node => chain info)
  $node->{_chain} = $self;
  $node->{_next} = undef;
  
  $self->{len} ++;

  $self;
  }

sub length
  {
  # Return the length of the chain in nodes. Takes optional
  # node from where to calculate length.
  my ($self, $node) = @_;

  return $self->{len} unless defined $node;

  my $len = 0;
  while (defined $node)
    {
    $len++; $node = $node->{_next};
    }

  $len;
  }

sub nodes
  {
  # return all the nodes in the chain as a list, in order.
  my $self = shift;

  my @nodes = ();
  my $n = $self->{start};
  while (defined $n)
    {
    push @nodes, $n;
    $n = $n->{_next};
    }

  @nodes;
  }

sub layout
  {
  # Return an action stack containing the nec. actions to
  # lay out the nodes in the chain, plus any connections between
  # them.
  my ($self, $edge) = @_;

  # prevent doing it twice 
  return [] if $self->{_done}; $self->{_done} = 1;

  my @TODO = ();

  my $g = $self->{graph};

  # first, layout all the nodes in the chain:

  # start with first node
  my $pre = $self->{start}; my $n = $pre->{_next};
  if (exists $pre->{_todo})
    {
    # edges with a flow attribute must be handled differently
    # XXX TODO: the test for attribute('flow') might be wrong (raw_attribute()?)
    if ($edge && ($edge->{to} == $pre) && ($edge->attribute('flow') || $edge->has_ports()))
      {
      push @TODO, $g->_action( _ACTION_CHAIN, $pre, 0, $edge->{from}, $edge);
      }
    else
      {
      push @TODO, $g->_action( _ACTION_NODE, $pre, 0, $edge );
      }
    }

  print STDERR "# Stack after first:\n" if $g->{debug};
  $g->_dump_stack(@TODO) if $g->{debug};

  while (defined $n)
    {
    if (exists $n->{_todo})
      {
      # CHAIN means if $n isn't placed yet, it will be done with
      # $pre as parent:

      # in case there are multiple edges to the target node, use the first
      # one to determine the flow:
      my @edges = $g->edge($pre,$n);

      push @TODO, $g->_action( _ACTION_CHAIN, $n, 0, $pre, $edges[0] );
      }
    $pre = $n;
    $n = $n->{_next};
    }

  print STDERR "# Stack after chaining:\n" if $g->{debug};
  $g->_dump_stack(@TODO) if $g->{debug};

  # link from each node to the next
  $pre = $self->{start}; $n = $pre->{_next};
  while (defined $n)
    {
    # first do edges going from P to N
    #for my $e (sort { $a->{to}->{name} cmp $b->{to}->{name} } values %{$pre->{edges}})
    for my $e (ord_values ( $pre->{edges}))
      {
      # skip selfloops and backward links, these will be done later
      next if $e->{to} != $n;

      next unless exists $e->{_todo};

      # skip links from/to groups
      next if $e->{to}->isa('Graph::Easy::Group') ||
              $e->{from}->isa('Graph::Easy::Group');

#      # skip edges with a flow
#      next if exists $e->{att}->{start} || exist $e->{att}->{end};

      push @TODO, [ _ACTION_TRACE, $e ];
      delete $e->{_todo};
      }

    } continue { $pre = $n; $n = $n->{_next}; }

  print STDERR "# Stack after chain-linking:\n" if $g->{debug};
  $g->_dump_stack(@TODO) if $g->{debug};

  # Do all other links inside the chain (backwards, going forward more than
  # one node etc)

  $n = $self->{start};
  while (defined $n)
    {
    my @edges;

    my @count;

    print STDERR "# inter-chain link from $n->{name}\n" if $g->{debug};

    # gather all edges starting at $n, but do the ones with a flow first
#    for my $e (sort { $a->{to}->{name} cmp $b->{to}->{name} } values %{$n->{edges}})
    for my $e (ord_values ( $n->{edges})) 
      {
      # skip selfloops, these will be done later
      next if $e->{to} == $n;

      next if !ref($e->{to}->{_chain});
      next if !ref($e->{from}->{_chain});

      next if $e->has_ports();

      # skip links from/to groups
      next if $e->{to}->isa('Graph::Easy::Group') ||
              $e->{from}->isa('Graph::Easy::Group');

      print STDERR "# inter-chain link from $n->{name} to $e->{to}->{name}\n" if $g->{debug};

      # leaving the chain?
      next if $e->{to}->{_chain} != $self;

#      print STDERR "#    trying for $n->{name}:\t $e->{from}->{name} to $e->{to}->{name}\n";
      next unless exists $e->{_todo};

      # calculate for this edge, how far it goes
      my $count = 0;
      my $curr = $n;
      while (defined $curr && $curr != $e->{to})
        {
        $curr = $curr->{_next}; $count ++;
        }
      if (!defined $curr)
        {
        # edge goes backward

        # start at $to
        $curr = $e->{to};
        $count = 0;
        while (defined $curr && $curr != $e->{from})
          {
          $curr = $curr->{_next}; $count ++;
          }
        $count = 100000 if !defined $curr;	# should not happen
        }
      push @edges, [ $count, $e ];
      push @count, [ $count, $e->{from}->{name}, $e->{to}->{name} ];
      }

#    use Data::Dumper; print STDERR "count\n", Dumper(@count);

    # do edges, shortest first 
    for my $e (sort { $a->[0] <=> $b->[0] } @edges)
      {
      push @TODO, [ _ACTION_TRACE, $e->[1] ];
      delete $e->[1]->{_todo};
      }

    $n = $n->{_next};
    }
 
  # also do all selfloops on $n
  $n = $self->{start};
  while (defined $n)
    {
#    for my $e (sort { $a->{to}->{name} cmp $b->{to}->{name} } values %{$n->{edges}})
    for my $e (ord_values $n->{edges})
      {
      next unless exists $e->{_todo};

#      print STDERR "# $e->{from}->{name} to $e->{to}->{name} on $n->{name}\n";
#      print STDERR "# ne $e->{to} $n $e->{id}\n" 
#       if $e->{from} != $n || $e->{to} != $n;		# no selfloop?

      next if $e->{from} != $n || $e->{to} != $n;	# no selfloop?

      push @TODO, [ _ACTION_TRACE, $e ];
      delete $e->{_todo};
      }
    $n = $n->{_next};
    }

  print STDERR "# Stack after self-loops:\n" if $g->{debug};
  $g->_dump_stack(@TODO) if $g->{debug};

  # XXX TODO
  # now we should do any links that start or end at this chain, recursively

  $n = $self->{start};
  while (defined $n)
    {

    # all chains that start at this node
    for my $e (sort { $a->{to}->{name} cmp $b->{to}->{name} } values %{$n->{edges}})
      {
      my $to = $e->{to};

      # skip links to groups
      next if $to->isa('Graph::Easy::Group');

#      print STDERR "# chain-tracking to: $to->{name} $to->{_chain}\n";

      next unless exists $to->{_chain} && ref($to->{_chain}) =~ /Chain/;
      my $chain = $to->{_chain};
      next if $chain->{_done};

#      print STDERR "# chain-tracking to: $to->{name}\n";

      # pass the edge along, in case it has a flow
#      my @pass = ();
#      push @pass, $e if $chain->{_first} && $e->{to} == $chain->{_first};
      push @TODO, @{ $chain->layout($e) } unless $chain->{_done};

      # link the edges to $to
      next unless exists $e->{_todo};	# was already done above?

      # next if $e->has_ports();

      push @TODO, [ _ACTION_TRACE, $e ];
      delete $e->{_todo};
      }
    $n = $n->{_next};
    }
 
  \@TODO;
  }

sub dump
  {
  # dump the chain to STDERR
  my ($self, $indent) = @_;

  $indent = '' unless defined $indent;

  print STDERR "#$indent chain id $self->{id} (len $self->{len}):\n";
  print STDERR "#$indent is empty\n" and return if $self->{len} == 0;

  my $n = $self->{start};
  while (defined $n)
    {
    print STDERR "#$indent  $n->{name} (chain id: $n->{_chain}->{id})\n";
    $n = $n->{_next};
    }
  $self;
  }

sub merge
  {
  # take another chain, and merge it into ourselves. If $where is defined,
  # absorb only the nodes from $where onwards (instead of all of them).
  my ($self, $other, $where) = @_;

  my $g = $self->{graph};

  print STDERR "# panik: ", join(" \n",caller()),"\n" if !defined $other;

  print STDERR 
   "# Merging chain $other->{id} (len $other->{len}) into $self->{id} (len $self->{len})\n"
     if $g->{debug};

  print STDERR 
   "# Merging from $where->{name} onwards\n"
     if $g->{debug} && ref($where);
 
  # cannot merge myself into myself (without allocating infinitely memory)
  return if $self == $other;

  # start at start as default
  $where = undef unless ref($where) && exists $where->{_chain} && $where->{_chain} == $other;

  $where = $other->{start} unless defined $where;
  
  # make all nodes from chain #1 belong to it (to detect loops)
  my $n = $self->{start};
  while (defined $n)
    {
    $n->{_chain} = $self;
    $n = $n->{_next};
    }

  print STDERR "# changed nodes\n" if $g->{debug};
  $self->dump() if $g->{debug};

  # terminate at $where
  $self->{end}->{_next} = $where;
  $self->{end} = $other->{end};

  # start at joiner
  $n = $where;
  while (ref($n))
    {
    $n->{_chain} = $self;
    my $pre = $n;
    $n = $n->{_next};

#    sleep(1);
#    print "# at $n->{name} $n->{_chain}\n" if ref($n);
    if (ref($n) && defined $n->{_chain} && $n->{_chain} == $self)	# already points into ourself?
      {
#      sleep(1);
#      print "# pre $pre->{name} $pre->{_chain}\n";
      $pre->{_next} = undef;	# terminate
      $self->{end} = $pre;
      last;
      }
    }

  # could speed this up
  $self->{len} = 0; $n = $self->{start};
  while (defined $n)
    {
    $self->{len}++; $n = $n->{_next};
    }

#  print "done merging, dumping result:\n";
#  $self->dump(); sleep(10);

  if (defined $other->{start} && $where == $other->{start})
    {
    # we absorbed the other chain completely, so drop it
    $other->{end} = undef;
    $other->{start} = undef;
    $other->{len} = 0;
    # caller is responsible for cleaning it up
    }

  print STDERR "# after merging\n" if $g->{debug};
  $self->dump() if $g->{debug};

  $self;
  }

1;
__END__

=head1 NAME

Graph::Easy::Layout::Chain - Chain of nodes for layouter

=head1 SYNOPSIS

	# used internally, do not use directly

        use Graph::Easy;
        use Graph::Easy::Layout::Chain;

	my $graph = Graph::Easy->new( );
	my ($node, $node2) = $graph->add_edge( 'A', 'B' );

	my $chain = Graph::Easy::Layout::Chain->new(
		start => $node,
		graph => $graph, );

	$chain->add_node( $node2 );

=head1 DESCRIPTION

A C<Graph::Easy::Layout::Chain> object represents a chain of nodes
for the layouter.

=head1 METHODS

=head2 new()

        my $chain = Graph::Easy::Layout::Chain->new( start => $node );

Create a new chain and set its starting node to C<$node>.

=head2 length()

	my $len = $chain->length();

Return the length of the chain, in nodes.

	my $len = $chain->length( $node );

Given an optional C<$node> as argument, returns the length
from that node onwards. For the chain with the three nodes
A, B and C would return 3, 2, and 1 for A, B and C, respectively.

Returns 0 if the passed node is not part of this chain.

=head2 nodes()

	my @nodes = $chain->nodes();

Return all the node objects in the chain as list, in order.

=head2 add_node()

	$chain->add_node( $node );

Add C<$node> to the end of the chain.

=head2 start()

	my $node = $chain->start();

Return first node in the chain.

=head2 end()

	my $node = $chain->end();

Return last node in the chain.

=head2 layout()

	my $todo = $chain->layout();

Return an action stack as array ref, containing the nec. actions to 
layout the chain (nodes, plus interlinks in the chain).

Will recursively traverse all chains linked to this chain.

=head2 merge()

	my $chain->merge ( $other_chain );
	my $chain->merge ( $other_chain, $where );

Merge the other chain into ourselves, adding its nodes at our end.
The other chain is emptied and must be deleted by the caller.
  
If C<$where> is defined and a member of C<$other_chain>, absorb only the
nodes from C<$where> onwards, instead of all of them.

=head2 error()

	$last_error = $node->error();

	$node->error($error);			# set new messags
	$node->error('');			# clear error

Returns the last error message, or '' for no error.

=head2 dump()

	$chain->dump();

Dump the chain to STDERR, to aid debugging.

=head1 EXPORT

None by default.

=head1 SEE ALSO

L<Graph::Easy>, L<Graph::Easy::Layout>.

=head1 AUTHOR

Copyright (C) 2004 - 2006 by Tels L<http://bloodgate.com>.

See the LICENSE file for more details.

=cut