: "@(#): shar.sh,v 2.1 1998/06/02 17:13:43 ahamm Exp $" #! /bin/sh # # This is a shell archive. # Remove everything above this line and run sh on the resulting file. # If this archive is complete, you will see this message at the end: # "All files extracted" # # Created on: Tue Apr 24 17:41:01 EST 2001 # Created by: ahamm at Sanderson Australia Pty Limited. # # Files archived in this archive: # README # ifx.tables # #-------------------- if [ -f README -a "$1" != "-c" ] then echo shar: README already exists else echo 'x - README (2859 characters)' sed -e 's/^X//' >README <<'SHAR-EOF' X XDESCRIPTION: X XThis script analyses .4gi files and outputs a 99.87% accurate listing Xof the tables used by the program, and whether they get SELECT INSERT XDELETE UPDATE operations applied to them. Perverse and deliberately Xtwisted string construction methods in the .4gl file (string constr- Xuction for PREPARE and DECLARE statements) can confuse the process, Xbut I've found that programmers tend to build strings from the left Xto the right. If yours don't, then I can't help you;-) X XThis program can be useful in two situations: when you don't have Xthe source code for a 4GL executable, or when you do not have rock- Xsolid procedures in place for programmers to document table usage Xin a program. We have built a call to this script into our program Xbuild scripts. Every time a programmer produces an executable, it Xautomagically lists the tables used what happens to them. X XIn 3 different applications with between 800 and 1600 individual Xprograms, you should be able to see how useful this is when we need Xto make table changes... X X XINSTALLATION: X XInstallation is trivial - just put ifx.tables into your favourite bin Xdirectory. PLEASE leave the Copyright and Author lines in place. It's Xonly good manners. The README file can go anywhere including /dev/null X X XUSAGE: X Xifx.tables [-fls] [-F] fname.4gi ... X Xwhere -f list individual 4gl files X -l list by locale - ie library or local filename X -s summary by table X -F no warning about missing filelist.RDS (FourGen concept) X XNOTE: if you don't know what kind of curse-word "filelist.RDS" is, Xthen you will always want to run it with the -F flag. X X XHOW IT WORKS: X XThis program uses the strings utility of UNIX to extract the strings Xcompiled into the 4GI. Amongst other things, this includes the strings Xused to build all SQL statements (nothing escapes!) and also all the Xstrings used by programmers to build prepared statements and cursors. XThe other key string written into the .4gi file is the name of the Xindividual .4gl files which constitute the program. X XThe output from strings is analysed by this script. Even strings Xwhich are constructed piece by piece can almost always be trapped: Xthe script uses a simple finite state machine to track the pieces. X X XDEPENDENCIES: X XThis script relies on plain vanilla Perl. It was originally written Xfor Perl version 4, then migrated to Perl 5. Current version in use Xis Perl 5.6. The script is so straight-forward that I can't imagine Xany future versions of Perl causing any trouble, although I've noticed Xthat the warnings get progressively more paranoid and fussy as new Xversions of Perl are released. If you get any spurious warnings with Xa new version of Perl, then remove the -w from the first line of the Xscript. The warnings will be wrnog wrnog wrnog! X XAndrew Hamm XSanderson Australia Pty Limited X-- XJust another 4GL hacker. SHAR-EOF chmod a=r,ug+w README if [ `wc -c ifx.tables <<'SHAR-EOF' X#!/usr/bin/perl -w X# Copyright (C) 1992-2001 Sanderson Australia Pty. Limited X# Author: Andrew Hamm X# Sccsid: @(#) .../bin/ifx.tables 1.7 2001/04/24 X$0 =~ s:.*/::; # tidy up the program name X Xuse strict; X Xuse constant SELECT => 1 << 0; Xuse constant INSERT => 1 << 1; Xuse constant DELETE => 1 << 2; Xuse constant UPDATE => 1 << 3; X X# printing codes X# keep them ordered inline with above Xmy @CODES = ("S", "I", "D", "U"); X X Xsub usage { X die <) X { X chomp; X X # our filelist.RDS is slightly different from original FourGen X # but I think (from memory) this should still work for FourGen X # X s:\)$::; # kicks in for Sanderson file format only X s:\(:/:; # kicks in for Sanderson file format only X X next unless m!(.*)/([^/]*)\.(4go)$!; X $filelist{$2} = "$1/$2.4gl"; X } X close FFD; X} X X X###################################################################### Xsub process ($*) { X###################################################################### X# X my ($iname, $INPUT) = @_; X my (%file, %location, %summary); X my ($file, $location, $action, $table); X my $state = 0; X X $iname .= ": " if $iname; X X STRING: while(<$INPUT>) X { X chomp; X next STRING if /@\(#\)/; # reject SCCS strings X X if(/(.*)\.4gl$/) # detect a member .4gl file X { X # attempt to convert file name to actual location X $file = $filelist{$1} || $_; X X # get separate components of filename X ($location = $file) =~ s:[^/]*$::; X X # trim trailing slash X $location =~ s:/$::; X X # must be local if not in a lib... (duh!) X $location = "LOCAL" unless $location; X X next STRING; X } X X $action = 0; X if(/\binsert\s+into\s+(\w+)/i) # detect insert statement X { X $action = INSERT; X $table = $1; X } X X if(/\bupdate\s+(\w+)\s+set\b/i) # detect update statement X { X $action = UPDATE; X $table = $1; X } X X if(/\bdelete\s+from\s+(\w+)/i) # detect delete statement X { X $action = DELETE; X $table = $1; X } X X if($action) X { X # clear "select" state if found other statement X # occasionally this means we may miss the odd select X # statement. Perhaps that is being too conservative? X $state = 0; X } X else X { X # bit of hard work detecting select statements, because X # they may be broken up. This can be due to programmers X # building strings over several expressions, so the X # results of this detection will never be perfect, but X # this is not a bad effort... X # X my $worked = 0; # in the beginning X X # detect (and remove) the select keyword X $worked = $state = 1 X if s:.*\bselect\b::i; X X # detect (and remove) FROM TABLE iff just seen a SELECT X # keyword X $worked = $state = 2 X if $state == 1 && s:.*\bfrom\s+(\w+).*::i; X X # bail out if nothing interesting found X # NOTE: the $state is NOT reset, because there may be X # interceding strings between the SELECT and the FROM. X next STRING X if ! $worked; X X if($state == 2) X { X $action = SELECT; X $table = $1; X $state = 0; X } X } X X if(defined $table) X { X # map table name 'cos it is case insensitive, but X # programmers aren't X $table =~ tr/A-Z/a-z/; X X $file{"$file $table"} |= $action if $action; X $location{"$location $table"} |= $action if $action; X $summary{"$table"} |= $action if $action; X } X } X # end of input loop X X if($fflag) # print file-level details? X { X foreach my $i (sort keys %file) X { X my $buf = ""; X $action = $file{$i}; X X foreach my $j (0, 1, 2, 3) X { $buf .= $CODES[$j] if $action & (1 << $j) } X X my ($f, $t) = ($i =~ m/(.*) (.*)/); X print $iname, $f, " " x (35 - length $f), X " ", $t, " " x (19 - length $t), X $buf, "\n"; X } X } X X if($lflag) # print locale-level details? X { X foreach my $i (sort keys %location) X { X my $buf = ""; X $action = $location{$i}; X X foreach my $j (0, 1, 2, 3) X { $buf .= $CODES[$j] if $action & (1 << $j) } X X my ($f, $t) = ($i =~ m/(.*) (.*)/); X print $iname, $f, " " x (23 - length $f), X " ", $t, " " x (19 - length $t), X $buf, "\n"; X } X } X X if($sflag) # print summary info only? X { X foreach my $i (sort keys %summary) X { X my $buf = ""; X $action = $summary{$i}; X X foreach my $j (0, 1, 2, 3) X { $buf .= $CODES[$j] if $action & (1 << $j) } X X print $iname, $i, " " x (19 - length $i), $buf, "\n"; X } X } X} # process X X X# Sanderson has a multi-pcode environment - see below Xmy $PCODE = $ENV{"PCODE"}; Xuse vars qw(*STDIN *INPUT); X X# NOTE: you must pipe from strings if you want to filter! Xprocess "", \*STDIN unless @ARGV; X Xforeach my $iname (@ARGV) # spin over all named executables X{ X # Sanderson special treatment: we have a multi-version PCODE X # environment, which relies on scripts and special cheats X # X $iname = "$iname/$PCODE.4gi" X if defined $PCODE && -d $iname; X X if(open INPUT, "strings $iname |") X { X process $iname, \*INPUT; X close INPUT; X } X else X { warn "cannot execute strings $iname: $!\n" } X} SHAR-EOF chmod a=rx,ug+w ifx.tables if [ `wc -c