Monthly Scripting Column for March

Greetings! This continues a series of bulletins, in which we introduce one of the scripts that will be in a future release.

Tip: Let someone else parse the data.
'p4 -Ztag' and 'p4 -G' are good places to start, and P4Ruby and P4Perl provide API access via loadable modules.

Today's script

Perl has its detractors, but it's handy.

We present oldclients.pl, a script that finds the clients whose owners aren't in list that you get from "p4 users". (These owners are probably just old users who've been deleted, but the owner field in the client spec retains the original owner's name. It's no big deal, but offends our sense of order.)

The challenge is to do it with as few calls to 'p4' as possible. We know we need the list of clients and the list of users, which is two calls to 'p4'. Do we need any more than that?

Parsing output

Most of the commands have an alternate way of invoking them:
   p4  -Ztag  clients
This returns "tagged" output - this column gave an example of it two months back in some P4Perl code - and tells Perforce to let you have output that looks like this:
... client heart
... Update 1001544831
... Access 1081295702
... Owner arthur
... Options noallwrite noclobber nocompress unlocked nomodtime normdir
... Root /tmp/home/examples/heart
... Host 
... MapState 1
... Description 

That's the information for one entry, which means one client workspace. (An empty line separates each client's data.)

Look at this closely.

  1. The first thing on the line is the "tag".
  2. the rest of the line is specific to the tag, for example the dates are stored as an integer (seconds since early 1970, if you must know).
For example, this refers to client "heart", which has no "Host" entry. Arthur's the "Owner".

We've written a small routine, ztag.pl, to parse this for the simplest cases ('p4 files', 'p4 users', etc). It's used in our script, thusly:

require "ztag.pl";
$p4 = "p4 -Ztag -u zaphod";

$client_tagged_cmd = "$p4 users";
@ret = readinZtag($client_tagged_cmd);

foreach $u (@ret) {
  $userName = $u->{'User'};
  print "$userName\n";
}

First step: Figuring out what data you need

In this case:

  1. For the list of users, run "p4 -Ztag users" ;
  2. For the list of clients, run "p4 -Ztag clients".

Take a moment to notice that "p4 -Ztag clients" has a lot of information in the tagged output. The options are there, so writing a script to report which clients have 'compress' enabled will need one call to 'p4'. (In fact, the Perforce example database has such a script.)

Second step: Piece together the program

The program is below. The basic flow is:

  1. Get the list of users;
  2. Get the list of clients (and respective owners, of course);
  3. For each of the clients:
  4. See if the owner is on the user-list. Print what you find.
Note that we are cautious about how we check that user-list. The Perl construct, "defined", is helpful because it doesn't create anything as a side-effect of looking.
Reminder: Green text for Perforce hooks.

# Task: compare client specs to users, and flag the
#       client specs owned by users that don't exist anymore.

#
# num of calls to 'p4': 2
# status:   tested on Win/2000 using perl 5.6
#
# room for optimization/improvement: add getopts call
#
# Copyright 2004 Perforce Corporation, Inc. All rights reserved.

require "ztag.pl";
$p4 = "p4 -Ztag -u zaphod";

#-----------------------------------------------------------
# first call to P4: 'p4 users'
#-----------------------------------------------------------
$client_tagged_cmd = "$p4 users";
@ret = readinZtag($client_tagged_cmd);

%userHash = {};
foreach $u (@ret) {
  $userName = $u->{'User'};
  $userHash{$userName} = $u;
}
#-----------------------------------------------------------
# second call to P4: 'p4 clients'
#-----------------------------------------------------------
$client_tagged_cmd = "$p4 clients";
@ret = readinZtag($client_tagged_cmd);

foreach $c (@ret) {
  $clientName = $c->{'client'};
  $clientOwner = $c->{'Owner'};

  if (defined($userHash{$clientOwner})) {
    print "$clientName owned by $clientOwner OK\n";
  } else {
    print "$clientName owned by $clientOwner ** unknown user **\n";
  }
}

Note: all the programs shown in these columns have been written four times: in Perl, in P4Perl, in Python, and in P4Ruby. Look into the Perforce example database for the other versions. $Id: //guest/jeff_bowles/scripts/0330perl.html#1 $
© 2004 Perforce Corporation, Inc.