#! /usr/bin/perl -w use strict; =head1 Notices Originally developed for Perforce by VIZIM (www.vizim.com) Copyright (c) 2014 Perforce Software, Inc. and VIZIM Worldwide, Inc. All rights reserved. Please see LICENSE.txt in top-level folder of this distribution for license information =cut use v5.14.0; # Earliest version testing was performed against my $APPNAME = 'ctrlUManager.pl'; my $APPWHAT = 'ctrl file user name manager; version 2.03'; my $APPNOTICES = "Copyright (c) 2014 Perforce Software, Inc. and VIZIM Worldwide, Inc. All rights reserved. See LICENSE.txt for license information."; =head1 User name manager Tool to manage user names in ctrl files. =cut =head2 Name list mode With one argument the tool operates in list mode. The file specified by the argument is a control file. Output is the total number of times each unique name is encountered. =cut =head2 Name mapping mode With two arguments the tool operates in mapping mode. The file specified by the first argument is the control file. The file specified by the second argument is the name mapping specification. Output is the content of the control file with all user name attribute values mapped as specified. If the name is not specified then standard user name mapping is performed. =cut if( defined $ARGV[0] && ($ARGV[0] =~ m!^\-+V!) ) { print "$APPWHAT\n"; exit(0); } if( ! defined $ARGV[0] || $ARGV[0] =~ m!^\-+[h\?]! ) { print "$APPWHAT $APPNOTICES Usage: $APPNAME -V $APPNAME [-h|-?] $APPNAME ctrl $APPNAME ctrl mapping One argument is listing mode. Two arguments is mapping mode.\n"; exit( 0 ); } if( ! -e $ARGV[0] ) { print "ctrl file does not exist - $ARGV[0]\n"; exit( 1 ); } my $hIn = undef; open $hIn, '<', $ARGV[0] or die "Can't open $ARGV[0] - $!"; my $modeMapping = exists $ARGV[1]; if( $modeMapping && ! -e $ARGV[1] ) { print "Mapping file does not exist - $ARGV[1]\n"; exit( 1 ); } =head3 Enforced name mappings Names that don't occur within the mapping file and names proposed by the mapping file are subject to processing against recommended Perforce user name characters. Space characters as well as the characters \ / [ and ] in a user name are replaced by the underbar (_) character. =cut my %mappings = (); sub MapName($) { my $name = $_[0]; return $mappings{$name} if exists $mappings{$name}; $name =~ s![\s\\\/\]\[]!_!g; return $name; } =head3 Mapping file format Each line within the mapping file is processed individually. Blank lines, lines that contain only space characters, and lines that have # as the first non-blank character are ignored as comments. All other lines are expected to follow the pattern: old\tnew where old is the old name, \t is the tab character, and new is the new name. Regardless of what is specified as the new name that name is subject to enforced name mappings before being established as the mapping for the old name. =cut if( $modeMapping ) { my $hMap = undef; open $hMap, '<', $ARGV[1] or die "Can't open $ARGV[1] - $!"; while(<$hMap>) { chomp; next if m!^\s*$!; next if m!^\s*\#!; my ($old, $new) = (undef, undef); ($old, $new) = split "\t"; if( ! defined $old ) { print "Don't understand mapping - $_\n"; close $hMap; exit( 1 ); } $mappings{$old} = MapName($new); } close $hMap; } my %users = (); while (<$hIn>) { if( m!^(\s*\<.+ user\=\")([^\"]+)(\".*)$! ) { my ($before, $name, $after ) = ($1, $2, $3); if( $modeMapping ) { print $before, MapName( $name ), $after, "\n"; } else { ++$users{$name}; } } elsif( $modeMapping ) { print $_; } } if( ! $modeMapping ) { foreach my $user (sort keys %users) { print sprintf "%6d %s\n", $users{$user}, $user; } }