#!/usr/local/bin/perl
#
# DosAdmin -- Front-End for MS-DOS Incoming Files Test & Move
# V1.6 pl 05 - 27 Feb 1999
#
#
# Written by
# Jouko Valta (jopi@x2ftp.oulu.fi)
#
# Based on "msdosrecent",
# written for x2ftp.oulu.fi by Jani "Flame/Pygmy Projects" Vaarala
#
#
# Revision History
# V1.0 pl 00 - 11 Jul 1996
# Checks for bad files, folds filenames, and strips box ads.
# Scans .txt files to determine category, and dissolves .zip files.
# to appropriate subdirectories.
#
# V1.1 pl 00 - 19 Jul 1996
# Create 00add -index file for each category dissolved.
# Skip files newer than the preset minimum age (5 min).
#
# V1.2 pl 00 - 29 Aug 1996
# Added .ARC support
# Added -help option.
# Collect long description and use it if no 'Short:' specified.
# Recursively dissolve any archives found inside archives.
# Option to force dissolving to a specific directory instead.
# Ensures correct group (ftpadm) for dissolved files.
#
# V1.3 pl 00 - 14 Oct 1996
# Enhanced duplicate handling methods.
# Fixed bug in printing 'Short:' lines containing a '%'.
# Create log of checked incoming files.
#
# V1.3 pl 01 - 18 Nov 1996
# Check that given Category is valid.
# Several bug fixes in internal operation. (Oops!)
#
# V1.3 pl 02 - 31 Dec 1996
# Internal operation slightly developed.
# Create ZIP comment file.
# -scan and -bug options to allow debugging.
#
# V1.4 pl 00 - 17 Jan 1997
# All log files moved from /var/tmp to /pc/log.
# Optionally evaluate archive contents while dissolving.
#
# V1.4 pl 01 - 12 Feb 1997
# Return values for functions declared.
# Upon start, check current directory really is some FTP directory.
# Check that Log Files are writeable.
# File contents evaluation improved.
#
# V1.5 pl 00 - 21 Feb 1997
# Status Log File format 2.0.
# Use Blacklist.
# Strict (-S -U) options.
# TXT files may have other extensions.
# TXT parser accepts certain uppercase keywords and implied Category.
# Create Keywords log.
# Most of MOVE code written.
#
# V1.5 pl 01 - 4 Apr 1997
# Options for CD-ROM database control
# MOVE cheks that file has been dissolved first.
# Write file contents evaluation to the comments file.
# Write Long Description file.
# On dissolve, file type recognition enhanced.
#
# V1.5 pl 02 - 30 Apr 1997
# Dissolve all files that match the txtfile.
# Dissolve files without any txtfile to notxt/.
# Absolute directory path handling corrected on dissolve.
# Check the 'Replaces:' field.
#
# V1.5 pl 03 - 23 May 1997
# Heavy debugging done.
# Protected the scanner against binary format .DOC files.
# Option to create text file if one is not provided.
#
# V1.5 pl 04 - 16 Jun 1997
# General debugging and improved messages.
#
# V1.5 pl 05 - 9 Aug 1997
# Scanner and Category solver debugging plus improved messages.
#
# V1.5 pl 06 - 27 Sep 1997
# Error in 'tr' command jammed the Scanner with CreateText mode.
# Scanner debugging still continued.
# LHA & Zoo Syntax corrections.
#
# V1.5 pl 07 - 31 Oct 1997
# ZIP Filename extraction fixed on Dissolve.
# Improved processing messages.
#
# V1.6 pl 00 - 15 Nov 1997
# Write X2 Template to the original (or auto-created) textfile.
#
# V1.6 pl 01 - 26 Nov 1997
# Recursive dissolving routines re-structured.
# Dissolve writes 00status file for each category.
#
# V1.6 pl 02 - 23 Feb 1998
# Minor patches to prevent 'tr -d' from jamming.
# Smooth adjustments in logging Diagnostic Output.
#
# V1.6 pl 03 - 04 Apr 1998
# Registration forms recognized better.
#
# V1.6 pl 04 - 27 Apr 1998
# Parser did not set any error code for unrecognized category.
#
# V1.6 pl 05 - 27 Feb 1999
# "CDRom" Keyword changed to "CDRom Permission".
# DOS-readable Status and Remark filenames.
# Print all Remarks on a Collection file by option (default ON).
# New Keyword variations added to scanner (Creator, Written by).
# .
#
#
# Future
# Implement Move file with CD-ROM database updating
#
#
# This script is a front-end which scans files in INCOMING and reports
# their status. After virus check on PC, the counterpart of this script
# will be used for moving each file onto place, and sending reports to
# uploaders.
# Most of the file testing and other processing is performed by other
# programs, specificly developed for each task.
#
#
$Usage = "\
Options:\
-h show help page and exit\
-bug quick: skip all tests (not recommended)\
-b do blacklist\
-B specify blacklist file\
-scan just test scan routines, don't move anything\
-t just test files, don't move anything\
-C,-cd update CD-ROM database. (default true on move)\
-f fold uppercase to lowercase, warn if file exists (Default)\
-F fold uppercase to lowercase, overwrite if older file exists\
-ascii,-g Filter out DOS Graphics (Default)\
-I Update index files only (dissolve files to /dev/null)\
-i Interactive Move and rm (Default)\
-k Update Keywords Database (Default)\
-K Rebuild Keywords Database\
-D force another Category to place extracted files onto\
-e evaluate extracted files types\
-x dissolve files under /pc/ for virus check on PC. (Default)\
-X force dissolve\
-m move files (interactive)\
-M force move files (interactive)\
-r replace old files (use: -f -r or -m -r) (Default)\
-all find File Updates automatically (enables -r)\
-S strict mode: Key Fields required.\
-U strict mode: process updates only.\
-T Scan text files only. No zip files checked.\
-p Pack archive remark files. (Default)\
-o change owner of each file\
-v verbose mode.\
-V verbose mode. Also affects subprocesses used.\
-W Specify alternate workdir.\
-nb no blacklist\
-nC don't remove old CD-ROM entries.\
-ne ignore filetypes on extract\
-nf no name folding\
-ng keep any DOS Graphics\
-ni Non-interactive Move and rm\
-nk Do not rebuild Keywords Database\
-np Suppress printing packed archive remark file.\
-nt don't test for archive integrity\
-nl,-nu don't scan any upload log files\
-nx no dissolving\
-nm no move (Default)\
-nr don't replace files\
-ns,-nz don't strip Zip Comments and ads\n\
Quick re-scan: -T -R -D
(-nb) -nt -nr -nf -nl (-ns)\n\n";
# -----------------------------------------------------------------------------
# Filenames
#
$YEAR = `/bin/date +%y`; # Year in 2 digits.
$PCWORK = "/pc/prg"; # Where to dissolve files
#$PCWORK = "/tmp/debug"; # Where to dissolve files (debugging)
# NOTE: all files in this dir are removed!
$LOGHOME = "/pc/log";
if (! -d $LOGHOME || ! -w $LOGHOME) {
printf STDERR "Cannot access $LOGHOME. Please, check the setting or run\n create_dosadm_logs script.\n";
$LOGHOME = "/var/tmp";
}
$UPLOADLOG = $LOGHOME . "/uploadlog";
$STATUSFILE = $LOGHOME . "/status";
$NOTESLOG = $PCWORK . "/00status.txt";
$UPDATESLOG = $PCWORK . "/00updates.txt";
$GlobalCommentFile = $PCWORK . "/00remarks.txt";
$TESTLOG = $LOGHOME . "/dostestlog";
$KEYWORDLOG = $LOGHOME . "/keywords";
$BLACKLIST = $LOGHOME . "/blacklist"; # removed files not wanted again
$REJECTLOG = $LOGHOME . "/rejected.$YEAR";
# $REJECTLOG = "/ftp-service/ftp/pub/msdos/programming/x2info/rejected.$YEAR";
$INDEXDIR = $LOGHOME . "/programming"; # Re-formatted TXT files.
$REUPLOADDIR = "/ftp-service/ftp/incoming/msdos/reupload_these";
$HOLDDIR = $REUPLOADDIR;
$TEXTDIR = "/src/txt";
$REJECTDIR = "/src/junk";
$FTPDIR = "/ftp-service/ftp/pub/msdos";
$FTP_INCOMING = "/ftp-service/ftp/pub/msdos/incoming";
$CDROMDIR = "/ftp-service/cdrom/pub/msdos/programming";
$BIN = "/ftp-service/bin/";
$ZIPTEST = $BIN . "ziptest";
$ZIPCOMMENT = $BIN . "zipcomment -i -b -sa -log $TESTLOG";
$UPDATE_INDEX = $BIN . "doit";
$UPLOADER = $BIN . "uploader -f -u";
$MAILTEST = $BIN . "validaddr";
$LOCATE = "/local/bin/locate";
$FIND = "/bin/find \. -type f -print | /bin/cut -c3-";
$UNARC = "/usr/local/bin/arc x ";
$UNARJ = "/usr/local/bin/unarj e "; # 'x' fails on the demo version
$UNLZH = "/usr/local/bin/lha ";
$UNZIP = "/usr/local/bin/unzip -L "; # Target directory support
$UNZOO = "/local/bin/zoo xd ";
###
$UNRAR = "/pc/bin/rar200.exe";
# -----------------------------------------------------------------------------
# Declare Status Constants
#
$VS_NONE = 0;
$VS_OK = 1;
$VS_INFECTED = 2;
$AS_NONE = 0;
$AS_OK = 1;
$AS_CORRUPT = 2;
$AS_FORMAT = 3;
$AS_TAMPER = 4;
$DS_NONE = 0; # File Dissolve Modes
$DS_DIS = 1;
$DS_CLEAR = 2;
$DS_NEW = 3; # Clear PCWORK and dissolve files
$DS_COUNT = 4;
$DS_SCAN = 8;
## Declare CDROM Constants
$CD_NONE = 0;
$CD_YES = 1;
$CD_FREE = 2;
$CD_NONPROFIT = 3;
$CD_GPL = 4;
$CD_DEMO = 5; # Demo version, not for CD
$CD_PERM = 6; # Prior permission required
$CD_NO = 7;
$CD_CONFL = 8; # Claimed yes, but contents seem suspicious
$CD_ASKED = 16;
@CDPrefix = ( "", "ON_", "ON_", "ON_", "", "OFF_", "OFF_", "OFF_",
"OFF_", "", "", "", "", "", "", "",
"ASKED_"
);
@CDString = ( "n/a", "Yes", "Yes", "Yes", "n/a", "No", "No", "No",
"No", "n/a", "n/a", "n/a", "n/a", "n/a", "n/a", "n/a",
"n/a"
);
# Declare Return Values for Test Functions
$BAD_FILE = -1;
$BAD_FILETYPE = -2;
$DOC_BINARY = -3;
$NO_MAINFILE = -4;
$NO_TXTFILE = -5;
$KEYSMISSING = -6;
$NO_CATEGORY = -7;
$BAD_CATEGORY = -8;
$BAD_CDVALUE = -9;
$FILE_EXISTS = -10;
#
$E_NONE = 0;
$FILE_OK = 0; # Same as $E_NONE
$FILE_SKIP = 1; # was it moved ?
$MAIN_IS_TXT = 2;
# Move
$FILE_MOVE = 4;
$FILE_HOLD = 5; # re-upload requested
$FILE_JUNK = 6;
$AltCategory = "";
$rmopt = "";
$Debug = 0; # Currently no option provided.
$TestMode = 0;
$TestScan = 0;
$Verbose = 2;
$Blacklist = 1;
$FoldNames = 1; # Try to fold filenames: 0= no, 1= yes, 2= Force
$DosGraphics = 1;
$Dissolve = $DS_DIS;
$Move = 0;
$CDupdate = 0; # Off, as we need it as a command flag.
$DoReplace = 0;
$Comments = 1;
$Owner = 1;
$TestIntegr = 1;
$ScanLogs = 1; # 1 = update log, 2 = don't
$Build_KWords = 1;
$EvaluateFiletypes = 1; # Flag file types included
$CreateText = 1; # Create text file if one is not provided.
$AskMv = 1;
$Strict = 0; # If set, process GOOD files only.
$MaxDescLen = 512; # Max length of Short Description
# -----------------------------------------------------------------------------
# set path
# $home=$ENV{HOME} || die "No HOME, are you homeless?\n";
$user=`/usr/ucb/whoami` || die "You don't exist.\n";
chop $user;
$cwd = `/bin/pwd`;
chop $cwd;
# print $cwd;
### process any FOO=bar switches
eval '$'.$1.'$2;' while $ARGV[0] =~ /^([A-Za-z_]+=)(.*)/ && shift;
### Command line options
while ($_ = $ARGV[0], /^-/) {
shift;
last if /^--$/;
if (/^-h/) { print $Usage; exit 2; }
elsif (/^-scan/) { $TestMode = 0; ++$TestScan;
++$EvaluateFiletypes;
next; }
elsif (/^-t/) { ++$TestMode; next; } # Only test files
elsif (/^-bug/) { $Comments = 0; $FoldNames = 0; $DoReplace = 0;
$ScanLogs = 0; $TestIntegr = 0;
$PCWORK = "/pc/debug";
$STATUSFILE= $PCWORK . "/status"; # not same as "00status" !
$TESTLOG = $PCWORK . "/dostestlog";
next; }
elsif (/^-b/) { ++$Blacklist; next; }
elsif (/^-B/) { $BLACKLIST = $ARGV[0]; shift; next; }
elsif (/^-C|^-cd/) { $CDupdate = 1; next; } # command for CD updating
elsif (/^-D/) { $AltCategory = $ARGV[0]; shift; next; }
elsif (/^-e/) { $EvaluateFiletypes = 1; } # flag file types included
elsif (/^-f/) { $FoldNames = 1; next; }
elsif (/^-F/) { $FoldNames = 2; next; }
elsif (/^-ascii|^-g/) { $DosGraphics= 1; next; } # remove DOS graphics
elsif (/^-i/) { $AskMv = 1; next; }
elsif (/^-k/) { $Build_KWords = 1; next; } # update Keywords file
elsif (/^-K/) { $Build_KWords = 2; next; } # rebuild Keywords file
elsif (/^-x/) { $Dissolve = $DS_DIS; next; } # extract archives
elsif (/^-X/) { $Dissolve = $DS_NEW; next; }
elsif (/^-I/) { $Dissolve = $DS_COUNT; next; } # only update indexes
elsif (/^-T/) { $Dissolve = $DS_SCAN; next; } # re-scan txt files
elsif (/^-m/) { $Move = 1; next; } # move files (interactive)
elsif (/^-M/) { $Move = 2; next; } # force move files
elsif (/^-R/) { $CreateText= 1; next; }
# Note: -R requires 'EvaluateFiletypes' set as well.
elsif (/^-r/) { $DoReplace = 1; next; }
elsif (/^-a/) { $DoReplace = 2; next; } # find updates on FTP
elsif (/^-S/) { $Strict = 1; ++$Blacklist; next; } # key fields required
elsif (/^-U/) { $Strict = 2; ++$Blacklist; next; } # process updates only
elsif (/^-o/) { $Owner = 1; next; } # change owner upon move
elsif (/^-q/) { $Verbose = 0; next; } # quiet mode
elsif (/^-v/) { $Verbose = 1; next; } # verbose mode
elsif (/^-V/) { $Verbose = 2; next; }
elsif (/^-W/) {
$PCWORK = $ARGV[0]; shift; # where to dissolve files
if (! -d $PCWORK || ! -w $PCWORK) {
printf STDERR "Cannot access work directory $PCWORK.\n";
exit 2;
}
$NOTESLOG = $PCWORK . "/00status";
$UPDATESLOG = $PCWORK . "/00updates";
next;
}
elsif (/^-p/) { $GlobalCommentFile = $ARGV[0]; shift; next; }
elsif (/^-np/) { $GlobalCommentFile = ""; next; }
elsif (/^-nb/) { $Blacklist = 0; next; }
elsif (/^-nC/) { $CDupdate = 2; next; } # keep old CD-ROM entries
elsif (/^-ne/) { $EvaluateFiletypes = 0; } # don't check file types
elsif (/^-nf/) { $FoldNames = 0; next; } # no name folding
elsif (/^-ng/) { $DosGraphics= 0; next; } # keep DOS graphics
elsif (/^-ni/) { $AskMv = 0; $rmopt = ""; next; }
elsif (/^-nk/) { $Build_KWords = 0; next; } # don't update Keywords
elsif (/^-nl/) { $ScanLogs = 0; next; } # don't read xferlogs
elsif (/^-nu/) { $ScanLogs = 2; next; } # don't update uploadlog
elsif (/^-nm/) { $Move = 0; next; } # don't move files
elsif (/^-nR/) { $CreateText= 0; next; }
elsif (/^-nr/) { $DoReplace = 0; next; } # don't replace files
elsif (/^-ns|^-nz/) { $Comments = 0; next; } # skip ZipComment stripper
elsif (/^-nt/) { $TestIntegr= 0; next; } # no integrity testing
elsif (/^-nx/) { $Dissolve = 0; next; } # no dissolving
elsif (/^-/) { die "Unsupported argument near $_\n Stopped"; }
}
if ($TestMode) { #
$FoldNames = 0;
$Dissolve = 0;
$Move = 0;
$DoReplace = 0;
}
if ($TestScan) { # Note: This option disabled TestMode
$TestIntegr = 0;
$FoldNames = 0;
$Comments = 0;
$DoReplace = 0;
# $ScanLogs = 0;
}
if ($AltCategory) { # User-specified override, i.e. for no *.txt's
$err = check_dos_category($AltCategory);
}
(! ($cwd =~ /ftp|src|warez/) || ($cwd =~ /cdrom/)) &&
die "\nNot FTP path... where are you ? Panic stop.\n\n";
# print "Group ID $)\n";
@ChkMark = ( " ", "x", "2", "3", "4", "5", "6", "7", "8", "9" );
if ($Verbose) {
printf ("\n Parameters\n");
printf ("\t[$ChkMark[$TestMode]] Test only\n");
printf ("\t[$ChkMark[$TestIntegr]] Test Archives\n");
printf ("\t[$ChkMark[$FoldNames]] Folding\n");
printf ("\t[$ChkMark[$Comments]] Comments\n");
printf ("\t[$ChkMark[$Dissolve]] Extract\n");
printf ("\t[$ChkMark[$Move]] Move\n");
printf ("\t[$ChkMark[$DoReplace]] Replace\n");
printf ("\t[$ChkMark[$ScanLogs]] Xferlogs\n");
printf ("\t[$ChkMark[$EvaluateFiletypes]] Scan Archive Contents\n\n");
printf ("\t[$ChkMark[$CreateText]] Create Text\n");
printf ("\t[$ChkMark[$Build_KWords]] Update Keywords\n");
printf ("\tLogFile\t\t$TESTLOG\n");
printf ("\tWorkDir\t\t$PCWORK/$AltCategory\n"); # Subdir can be empty.
printf ("\n");
}
# Check workspace exists
stat($PCWORK);
( -d _ && -r _ && -w _ && -x _ ) || die "Cannot access $PCWORK. Stopped";
stat($INDEXDIR);
( -d _ && -r _ && -w _ && -x _ ) || die "Cannot access $INDEXDIR. Stopped";
stat($STATUSFILE);
(! -e _ || (-r _ && -w _ )) || die "Cannot access $STATUSFILE. Stopped";
stat($TESTLOG);
(! -e _ || (-r _ && -w _ )) || die "Cannot access $TESTLOG. Stopped";
stat($NOTESLOG);
(! -e _ || (-r _ && -w _ )) || die "Cannot access $NOTESLOG. Stopped";
stat($BLACKLIST);
(! -e _ || (-r _ && -w _ )) || die "Cannot access $BLACKLIST. Stopped";
stat($REJECTLOG);
(! -e _ || (-r _ && -w _ )) || die "Cannot access $REJECTLOG. Stopped";
stat($KEYWORDLOG);
(! -e _ || (-r _ && -w _ )) || die "Cannot access $KEYWORDLOG. Stopped";
# Clear Work Directory if any of the options -S -U -X is specified.
if ($Strict || ($Dissolve & $DS_CLEAR)) {
printf STDERR "\nCleaning work directory '$PCWORK/'.\n";
printf STDERR "/bin/rm -rf $STATUSFILE $NOTESLOG $UPDATESLOG $PCWORK/*\n";
system ("/bin/rm -rf $STATUSFILE $NOTESLOG $UPDATESLOG $PCWORK/*");
system ("/bin/touch $STATUSFILE $NOTESLOG $UPDATESLOG");
system ("/bin/chgrp ftpadm $STATUSFILE $NOTESLOG $UPDATESLOG");
chmod 0664, $STATUSFILE;
chmod 0664, $NOTESLOG;
chmod 0664, $UPDATESLOG;
}
# Keywords log file.
if ($Build_KWords == 2) {
printf STDERR "\nBuilding new Keywords Log '$KEYWORDLOG'.\n";
rename ($KEYWORDLOG, "$KEYWORDLOG.old");
system ("/bin/touch $KEYWORDLOG; chgrp ftpadm $KEYWORDLOG");
chmod 0664, $KEYWORDLOG;
}
# -----------------------------------------------------------------------------
#
# Read the Blacklist prior to any file processing.
#
if ($Blacklist) {
open (B, "$BLACKLIST") || die "Cannot read Blacklist file.\n";
while () {
chop;
# print $_; # View processing...
if (/^\s*$|^!/) { next; } # Empty line or comment
if (/^\s*(\S+)\s+(\S+)\s+(.+)$/) {
$BlackUploader{$1} = $2;
$BlackEntry{$1} = $3;
next;
}
} # while
close B;
} # blacklist
# -----------------------------------------------------------------------------
# Move files onto target directory
#
# List of checked files and their categories is obtained from STATUSFILE
#
if ($Move) {
if (! $CDupdate) {
$CDupdate = 1; # Update CD-Rom -- Not optional here.
}
$err = move_mainloop();
exit $err;
} # Move
# -----------------------------------------------------------------------------
if ($CDupdate) {
(! $ARGV[0]) && die "Filename(s) to update must be specified.\n";
foreach $target (@ARGV) {
# Check path, add if none given
# /(\S+)[\/\\]([0-9A-Za-z_\+\-\~\^\$\|\!\%\*]+)/
# ($cat, $file) = ;
if (! $path) {
$path = $RealCategory;
}
# View processing...
printf "\t%s\t%s\t%s\n", $cat, $file, $CDString[$cd];
$err = check_dos_category($cat);
$err = update_cdrom($cat, $mainfile, $cd);
} # foreach
exit $err;
} # CDupdate
# -----------------------------------------------------------------------------
#
## Phase 1 Checking
#
# -----------------------------------------------------------------------------
# Test Files
# Both the archive integrity is tested, and the mode looked up in xferlog.
#
# Three arrays are created. $Integrity{} for Ziptest results, and $BinMode{}
# and $From{} for uploading records. Each array is indexed by the filename.
#
if ($ScanLogs) {
$opt = "";
if ($ScanLogs == 2) {
$opt = "-nw";
}
# UPLOADER is a program that runs a case-insensitive 'grep' on the xferlogs.
open (T, "$UPLOADER $opt incoming |") ||
die "Can't stat '$UPLOADER'. Stopped";
print STDERR "\nReading uploading logs...\n";
while () {
# $Debug && print $_; # View processing...
chop;
# Note: All accesses are logged on the real directory, /pub/msdos/incoming/
#
# /pub/msdos/incoming/pedit200.txt b 638 pb@vol * [Sat Mar 22 16:11:37 1997]
# /pub/msdos/incoming/pedit200.zip b 105547 pb@vol * [Sat Mar 22 16:11:53 1997]
# / file ascii size email realhost date
if (/msdos\/incoming\S*\/(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\[.+\])/) {
$BinMode{$1} = $2; # 'a' or 'b'
$FileSize{$1} = $3; # size when uploaded
$From{$1} = $4; # unofficial e-mail
$RealHost{$1} = $5; # real host if different
$UploadDate{$1} = $6; # upload date (ascii string)
$Debug &&
print "DEBUG '$1'\t$BinMode{$1}\t$From{$1}\t$RealHost{$1}\t$UploadDate{$1}\n";
}
}
close T;
} # ScanLogs
if ($TestIntegr) { # Archive integrity testing (required)
print STDERR "\nTesting archives\n";
# If there are filenames specified on command line, pass them trough.
# Otherwise, test all files in the current working directory.
open (T, "$ZIPTEST @ARGV |") || die "Can't open $ZIPTEST";
while () {
print $_; # View processing...
if (/^\s*ok\s+(\S+)/) {
$Integrity{$1} = 1;
next;
}
if (/^\s*ERROR\s+(\S+)/) {
$Integrity{$1} = -1;
next;
}
}
close T;
} # Integrity
# -----------------------------------------------------------------------------
#
# Create lookup table for old files
#
# Scan subdirectories (reupload, old_upload) for duplicates and build
# lookup-table for old files.
if ($DoReplace) {
$ROOTDIR = "/ftp-service"; # Work Dir to chdir to
$FTPDIRS = "ftp/pub/msdos";
$FIND_FTP = "/bin/find $FTPDIRS -type f -print";
printf "\n\nCreating lookup table for files on the FTP Archive.\n";
open (foo, "$FIND_FTP |") || printf "Can't excute '$FIND_FTP'";
while () {
chop;
if (/^\s*$/) { next; } # Empty line
elsif (/incoming\//) { next; } # Incoming directory
# Takes the last slash by default.
elsif (/ftp\/(\S+)\/(\S+)/) { # File in any FTP dir
$dir = $1;
$file = $2;
# $index= $dir ."/" . $file;
$FileTable{$file} .= "$dir "; # Add path found
}
} # while
while (<"$REUPLOADDIR/*">) {
if (/reupload_these\/(\S+)$/) { # filename
# print "re-requested file '$_'"; # View processing...
next;
}
} # while
while (<"old_uploads/*">) {
if (/old_uploads\/(\S+)$/) { # filename
}
} # while
} # DoReplace
# -----------------------------------------------------------------------------
#
## Initialize logging
#
# -----------------------------------------------------------------------------
# The log files will be cleared by option, or via removing releated lines when
# sending the final upload report.
open (L, ">> $TESTLOG") || die "Cannot open Test Log file. Stopped";
open (N, ">> $NOTESLOG") || die "Cannot open Check Log file. Stopped";
open (R, ">> $REJECTLOG") || die "Cannot open Reject Log file. Stopped";
# File Updates list
open (U, ">>$UPDATESLOG") || printf "*** Cannot open Updates Log file.\n";
## Scan any TXT,ZIP,LZH,ARJ Filenames
if ($ARGV[0]) {
open (foo, "/bin/ls -1 @ARGV |") || die "Can't list input files";
}
else {
open (foo, "$FIND |") || die "Can't open $FIND";
}
$date = `/bin/date`;
print L "\n\nRUN\t$cwd\t($user)\t$date\n"; # Easier to read the log file
printf "\n";
print N "\nX2 FileScan Log\t\t\t$date"; # LF included in date string.
print N "----------------------------------------------------------------------------\n";
print N "Status\tCDROM\tFTPdir\t\tFile\t\tKeywords [ Source ]\n";
print N "----------------------------------------------------------------------------\n\n";
# -----------------------------------------------------------------------------
#
## Phase 2 Pruning
#
# -----------------------------------------------------------------------------
#
# This loop checks each filename in the current directory, and folds them into
# lowercase when needed. Then an attempt is made to replace corrupt files with
# good ones. Succesfull opparation is flagged thus. Upon failure, request to
# reupload the file is flagged.
#
printf "\nSorting out files\n";
$FileCount = 0;
name: while () {
chop;
if (/^\s*$/) { next line; } # Empty line
if (/README.UPL|^00|\/00/) { next; } # Don't touch the instructions
if (/junk|txt\/|reupload/) { next; } # Ignore rejected files
# File types known:
# \.arc|\.arj|\.asm|\.lha|\.lzh|\.txt|\.com|\.doc|\.exe|\.lsm|\.nfo|\.diz|
# \.readme|\.pas|\.c|\.h|\.zip|\.zoo
@statit = stat $_;
# $size = $statit[7]; # ( / 1024)+1;
$mtime = $statit[9]; # Last modify time since the epoch
# ($sec,$min,$hour,$mday,$mon,$year,$wday,$yday,$isdst)=localtime($mtime);
# Skip files that may still be written to... (default limit 5 min)
if ((time - $mtime) < 300) { # 5*60 seconds
print "Skip $_ (minimum age not reached)\n";
next name;
}
$file = $_;
print "Checking $file\r"; # \r = CR, No linefeed
# print "DEBUG $file\t$BinMode{$file}\t$From{$file}\t$RealHost{$file}\t$UploadDate{$file}\n";
# Fold filenames to lowercase
if ($FoldNames) {
$oldfile = $file;
$file =~ y/A-Z\-\~\^\$\|\!\%\*/a-z________/;
$file =~ s/[\.\_]good$|[\.\_]new$//gi;
$file =~ s/\_\./\./g;
# Check if folding is needed, and check no files will be overwritten
# by accident.
if ($file ne $oldfile) {
@newstat = stat $_;
# $newsize = $newstat[7]; # ( / 1024)+1;
$newmtime = $newstat[9]; # Last modify time since the epoch
# Protect 'newname' files that may still be written to...
if ( $Integrity{$file} < 0 && (time - $newmtime) < 300) { # 5 min
printf "Skip $oldfile ('$file' has not reached minimum age)\n";
$Integrity{$file} = 0;
}
# newname zero size
if (! -e $file || -z $file ) {
printf "Rename '$oldfile' => '$file'\n";
printf L "FOLD\t%s\t%s\n", $file, $oldfile; # Log name change
rename_file ($oldfile, $file);
}
# old ok, newname fails
elsif ($Integrity{$oldfile} > 0 && $Integrity{$file} < 0) {
# 1 Tested OK.
# 0 Not tested
# -1 Test not passed.
printf "Replacing defect '$file' with '$oldfile'\n";
printf L "REPL $file\t$oldfile\n"; # Log all operations
rename_file ($oldfile, $file);
}
# old fails, newname either ok or fail
# elsif ($Integrity{$oldfile} < 0 && $Integrity{$file} > 0) {
elsif ($Integrity{$oldfile} < 0 ) {
# 1 Tested OK.
# 0 Not tested
# -1 Test not passed.
printf "Ignoring defect duplicate '$oldfile'\n";
printf L "RMDUP $file\t$oldfile\n"; # Log all operations
printf ("rm $rmopt -- $oldfile\n");
system("rm $rmopt -- $oldfile");
}
# files identical
elsif (`diff $oldfile $file` == 0) {
# 0 No differences were found.
# 1 Differences were found.
# >1 An error occurred.
printf "Duplicate identical '$oldfile' <=> '$file'\n";
printf L "RMDUP $file\t$oldfile\n"; # Log all operations
printf ("rm -- $oldfile\n");
system("rm -- $oldfile");
}
# both ok, but files differ
else {
# if ($FoldNames == 2) # Force
printf "***Files '$oldfile' and '$file' differ.\n";
}
} # name differs (case-sensitively)
} # Folding
# ---------------------------------------------------------------------
# Check BLACKLIST and REJECTDIR
# These are checked before requesting a re-upload...
if ($Blacklist) {
if ($BlackEntry{$file}) {
printf "*** $file is on Blaclist.\n";
printf "$file\t$BlackEntry{$file}\n";
printf N "BLACK\t$file\t$BlackEntry{$file}\n";
}
if (-e "$REJECTDIR/$file") {
printf "*** $file has been rejected once.\n";
printf "$file\t$BlackEntry{$file}\n";
printf "$file\t$BlackUploader{$file}\n";
printf N "JUNK\t$file\t$BlackUploader{$file}\n";
}
# junk ($src, $mainfile, $txtfile, $REJECTDIR);
# if ( ) {
# add_blacklist ($file, $Uploader);
# }
} # Blacklist
# ---------------------------------------------------------------------
# Filter out bad files. (Executed always.)
#
# Running this program on reupload_these directory cannot affect the
# files, because they won't pass the integrity test.
#
# First, filter out empty or bad copies and try to replace them.
# Request for reupload if no valid file available, or autojunk it if
# no uploader known.
if ((! -s $file) || $Integrity{$file} < 0) {
printf(STDERR"\nDetected bad file $file\n");
print ("rename ($file, $REUPLOADDIR/$file);\n");
rename ($file, "$REUPLOADDIR/$file");
# move txt file
# $t = get_txt_name($file, 0);
# rename ($t, "$REUPLOADDIR/$t");
# Log the file as "on hold"
write_keys ($file, -1); # Log key fields
printf L "HOLD\t%s\t%d\n", $file, $Integrity{$file};
printf N "HOLD\t%s\t%d\n", $file, $Integrity{$file};
next name;
} # bad
# ---------------------------------------------------------------------
# Do Logs
# printf L "STAT\t$file\t$size\n";
# ---------------------------------------------------------------------
if ($DoReplace) {
# Scan subdirectories (reupload, old_upload) for duplicates.
# However, do not remove .txt on .zip, and vice versa.
if ($FileTable{$file}) {
# while (<"$REUPLOADDIR/$file*">)
if (/reupload_these\/(\S+)$/) { # filename
print "*** replace re-requested file '$_' ***\n";
# print L "REPL $file\t$_\n"; # Log all operations
# print L "REQ-OFF\t$_\n"; # Remove from requests
# printf( "rm $rmopt -- $_");
# system( "rm $rmopt -- $_");
}
}
while (<"old_uploads/$file*">) {
if (/old_uploads\/(\S+)$/) { # filename
print "*** replace old file '$_' ***\n";
# print L "REPL $file\t$_\n"; # Log all operations
# printf( "rm $rmopt -- $_");
# system( "rm $rmopt -- $_");
}
}
# if (`diff $oldfile $file` == 0) {
# 0 No differences were found.
# 1 Differences were found.
# >1 An error occurred.
# print "Duplicate identical '$oldfile' <=> '$file'\n";
# print L "RMDUP $file\t$oldfile\n"; # Log all operations
# printf( "rm $rmopt -- $oldfile");
# system( "rm $rmopt -- $oldfile");
# }
} # DoReplace
++$FileCount;
} # while PC file
printf "\nTotal %d files.\n", $FileCount;
close (L);
chmod 0660, $TESTLOG;
system ("chgrp -f ftpadm $TESTLOG");
# -----------------------------------------------------------------------------
#
## Phase 3
#
# -----------------------------------------------------------------------------
# Weed out any garbage in the archives before going further with dissolving
# them...
#
if ($Comments) {
print STDERR "\nRunning ZipComment...\n";
# optional ZipComment parameters:
# -t => -nd -nz
# -V => -v
# The rest ( -i -b -log ) are already defined on the variable.
$op = "";
if ($TestMode) { $op = "-nd -nz"; }
if ($Verbose == 2) { $op .= " -v"; }
# If there are filenames specified on the command line, pass them trough.
# Otherwise, test all files in the current working directory.
#
# Note: ARGV may not match when 'FoldNames' is set.
system ("$ZIPCOMMENT $op @ARGV"); # kludge to make it interactive
} # Comments
# -----------------------------------------------------------------------------
#
## Phase 4 - Dissolve the files
#
# -----------------------------------------------------------------------------
# Finally, Dissolve the archives under $PCWORK/ or alternate directory
# specified with -D option.
#
# Flag Value Action
# 0 None
# DS_DIS Dissolve files
# DS_CLEAR Clear PCWORK.
# DS_NEW Clear PCWORK and dissolve files.
# DS_COUNT Extract Files to /dev/null
# DS_SCAN Scan old text files, no mainfiles checked
#
# Re-open the test log file
open (L, ">> $TESTLOG") || die "Cannot open Test Log file. Stopped";
if ($Dissolve) {
$FileCount = 0;
$ErrCount = 0;
$ArcCount = 0;
$TotalCount = 0;
## Scan any TXT,ZIP,LZH,ARJ Filenames
# If there are filenames specified on the command line, pass them trough.
# Otherwise, test all files in the current working directory.
#
# Note: ARGV may not match when 'FoldNames' is set.
if ($ARGV[0]) {
open (foo, "/bin/ls -1 @ARGV |") || die "Can't list input files";
}
else {
open (foo, "/bin/ls -1 *.* |") ||
die "Can't list input files";
}
open (STF, ">>$STATUSFILE") || die "Cannot write Test Status file $STATUSFILE";
printf STF "\nCWD\t$cwd\t$date\n";
printf STF "REMARK\tCdRom\tcat\tmainfile\ttxtfile\n\n";
open (K, ">>$KEYWORDLOG") || die "Cannot write Keywords file $KEYWORDLOG";
#
# Global Summary of Archives
if ($GlobalCommentFile) {
open (GLOBAL_CMT, ">$GlobalCommentFile");
}
print "------------------------------------------------------------\n\n";
print "Extracting Files...\n";
while() {
chop;
# Scan .TXT file
if (/\.txt|\.lsm|\.doc|\.diz|\.nfo|\.readme/i) {
# $txtfile = $_;
# $mainfile = &get_main_name($txtfile, 0);
# printf "\n\n"; # Begin new file
#
# if (! $mainfile) {
# printf STDERR "No mainfile for '$txtfile'. Skipping...\n";
# printf L "FAIL\t***\t\tNo mainfile for '$txtfile'.\n";
# printf N "FAIL\t\t***\t\tNo mainfile for '$txtfile'.\n";
# next;
# }
next;
} # TXT file
# The rest are assumed mainfiles.
# The selection may be restricted via Strict option.
if (/\.arc|\.arj|\.lha|\.lzh|\.zip|\.zoo|\.rar|\.exe|\.com/i ||
/\.asm|\.bas|\.pas|\.cc*$|\.hh*$|\.cpp|\.hpp|\.mod|\.s3m/i) {
$mainfile = $_;
$txtfile = get_txt_name($mainfile, 0);
printf "\n\n"; # Begin new file
# Handle Scan
clear_records();
if ($txtfile) {
scan_txt ($txtfile);
}
else {
# printf STDERR "No textfile for '$mainfile'. Skipping...\n";
printf STDERR "No textfile for '$mainfile'.\n";
printf L "FAIL\t%s\t\tNo textfile found.\n", $mainfile;
printf N "FAIL\t\t%s\t\tNo textfile found.\n", $mainfile;
# next;
$Category = "notxt";
$RealCategory = "notxt";
}
# Force alternate Category if no primary one given.
# This can be used when processing old FTP files.
if ($AltCategory) { # User-specified override, for no *.txt's
$Category = $AltCategory;
$RealCategory = $AltCategory;
}
if (! $Source && $SourceList) {
$Source = $SourceList;
}
# Validity Checking ...
$err = check_records ();
write_keys ($mainfile, 1); # Log key fields OK
# Strict -- process good files only.
if ($Strict) {
if ($err != $FILE_OK || ($Strict == 2 && !$ReplaceList)) {
printf "STRICT: '$mainfile': File skipped.\n";
$Debug && printf L "STRICT\t'$mainfile'\tFile skipped.\n";
DoError($DS_SCAN, $err);
next;
}
} # strict
if ($Dissolve != $DS_SCAN) {
if (dissolve ($mainfile) < $E_NONE) {
++$ErrCount;
DoError($Dissolve, $err);
} # error
# dissolve ($txtfile); # Move the .txt file
}
# Write Standardized Long Description file to the Database.
write_long_txt ($RealCategory, $mainfile, $txtfile);
# Write X2 Template to the original (or auto-created) textfile
# if the keyword fields were not provided.
if (!$txtfile || $err == $NO_TXTFILE || $err == $KEYSMISSING) {
$tnam = "$FTP_INCOMING/$mainfile.tmp";
print "\n==> Creating $tnam (error $err)\n";
print L "CREATE\t$mainfile\tCreating '$tnam'\n";
write_form ($tnam);
if (! open (LI, ">>$tnam")) {
print "Cannot write $tnam";
print L "ERROR\t$mainfile\tCannot write $tnam";
}
printf LI "\n----------------------------------------------------------------\n\n";
close LI;
if ($txtfile) {
system ("/bin/cat $txtfile >> $tnam");
rename $tnam, $txtfile;
}
else {
system ("/bin/cat $FTP_INCOMING/$mainfile.txt-create >> $tnam");
($n, $foo) = split (/\./, $mainfile, 2);
$txtfile = $n . ".txt";
print "DEBUG: textfile = '$txtfile'";
rename $tnam, $txtfile;
} # else
chmod 0660, $txtfile; # Ensure permissions.
} # Template
++$FileCount;
$ArcCount += $IF_Archive;
$TotalCount += $DisCount;
} # file processed
} # while
close STF; # Statusfile for dissolved archives.
# chmod 0664, $STATUSFILE;
close K;
# Sort Keywords
system ("/bin/sort -u < $KEYWORDLOG > $KEYWORDLOG.new");
rename ("$KEYWORDLOG.new", $KEYWORDLOG);
chmod 0664, $KEYWORDLOG;
printf STDERR "\nCompleted.\n";
# Permissions
if (($Dissolve & $DS_DIS)) {
printf "\nDissolved %d files with %d errors.\n", $FileCount, $ErrCount;
printf " Total %d Files, %d Nested archives.\n\n", $TotalCount, $ArcCount;
printf L "\nREM\tDissolved %d files with %d errors.\n", $FileCount, $ErrCount;
printf L "REM\tTotal %d Files, %d Nested archives.\n\n", $TotalCount, $ArcCount;
if ($GlobalCommentFile) {
close (GLOBAL_CMT);
chmod 0660, "$GlobalCommentFile";
}
printf STDERR "chgrp -Rf ftpadm $PCWORK\n";
system ("chgrp -Rf ftpadm $PCWORK");
system ("chmod -Rf g+rw $PCWORK");
} # File Dissolve mode
} # if Dissolve option
$date = `/bin/date`;
print L "END\t$date"; # Easier to read the log file
close L;
close N;
close R;
close U;
printf STDERR "Wrote Summary to $NOTESLOG.\n";
printf STDERR "done.\n";
exit 0;
# -----------------------------------------------------------------------------
# *** End of Main Program ***
# -----------------------------------------------------------------------------
#
#
sub rename_file {
($oldfile, $file) = @_;
rename($oldfile, $file);
$Integrity{$file} = $Integrity{$oldfile};
$BinMode{$file} = $BinMode{$oldfile};
$From{$file} = $From{$oldfile};
$FileSize{$file} = $FileSize{$oldfile}; # size when uploaded
$RealHost{$file} = $RealHost{$oldfile};
$UploadDate{$file} = $UploadDate{$oldfile};
$OldName{$file} = $oldfile;
}
sub get_main_name {
local ($txtfile, $seekflg) = @_;
local $mainfile = '';
if ($txtfile =~ /(\S+)(\.txt|\.lsm|\.doc|\.nfo|\.readme)$/i) {
# print "Search $txtfile => $1";
$hum = $1 . ".*";
while(<${hum}>) {
if ($txtfile ne $_) {
$mainfile = $_;
}
}
if (!$mainfile && $seekflg) { # Search from TXTINDEX
} # seek
} # txtfile
return $mainfile;
}
sub get_txt_name {
local ($mainfile, $seekflg) = @_;
local $txtfile = '';
($hum, $foo) = split (/\./, $mainfile, 2);
# print "\nSearch $mainfile => $hum";
if ($hum) {
$hum .= ".*";
while(<${hum}>) {
if (/(\S+\.)(txt|lsm|doc|diz|nfo|readme)$/i) {
$txtfile = $_;
}
}
if (!$txtfile && $seekflg) { # Search TEXTDIR
} # seek
} # hum
return $txtfile;
}
# -----------------------------------------------------------------------------
sub DoError {
local ($level, $errcode) = @_;
printf L "ERROR\t%s\t proc %s error %s\n", $mainfile, $level, $errcode;
printf N "ERROR\t%s\t proc %s error %s\n", $mainfile, $level, $errcode;
printf NC "ERROR\t%s\t proc %s error %s\n", $mainfile, $level, $errcode;
}
# -----------------------------------------------------------------------------
#
## TXT File Handling Subroutines
#
# -----------------------------------------------------------------------------
sub clear_records {
# Required or Automatic Fields.
$Category = "";
$RealCategory = "";
$Source = "";
$SourceList = "";
$Creator = "";
$Author = "";
$Uploader = ""; # or Maintainer
$Company = "";
$Short = "";
$Keywords = "";
$LongDescription = "";
$FormatsList = ""; # File Formats supported.
$DocsList = "";
$Cdrom = "";
$CdromStatus = $CD_NONE;
# Optional fields
$Title = "";
$Version = "";
$EnterDate = "";
$Platforms = "";
$Requires = "";
$CopyingPolicy = "";
$ReplaceList = "";
$LineCount = 0; # Number of lines in LongDescr
$PartNumber = 0;
$PartCount = 0; # Number of parts in total.
$LSMmode = 0;
if ($EvaluateFiletypes) { # Flag file types included
$IF_Readme = 0;
$IF_Docs = 0;
$IF_Miscinfo = 0;
$IF_Source = 0;
$IF_Exec = 0;
$IF_Config = 0; # Program settings data
$IF_Object = 0;
$IF_Winapp = 0;
$IF_Install = 0;
$IF_Data = 0; # General data file
$IF_Data_audio = 0;
$IF_Data_image = 0;
$IF_Archive = 0;
$IF_System = 0; # Non-distributable files
$IF_UnknownType = 0;
$IF_PublicDomain= 0;
$IF_Freeware = 0;
$IF_Cardware = 0;
$IF_Shareware = 0;
$IF_Commercial = 0;
$IF_Has_register= 0; # Anything with registration
} # evaluate
}
# -----------------------------------------------------------------------------
#
# Files affected
# L TESTLOG log/dostestlog
# N NOTESLOG work/00status
# NC NCname work/cat/00status
# R REJECTLOG log/rejected.xx
# STF STATUSFILE log/status
#
sub write_keys {
local ($file, $error) = @_;
# $RealCategory on korjattu kategoria ja $Category uploadaajan antama.
# Write Debug Log
printf L "MODE\t%s\t%s\t%s\t%s\t%s\n",
$file, $BinMode{$file},
$From{$file}, $RealHost{$file}, $UploadDate{$file};
printf L "MAIL\t%s\t%s\t%s\n", $file, $Uploader, $Author;
printf L "SHORT\t%s\t%s\n", $file, $Short;
printf L "SOURCE\t%s\t%s\n", $file, $Source;
printf L "CATEGORY %s\t%s\t%s\n", $file, $RealCategory, $Category;
$ReplaceList && printf L "REPLACE \t%s\t%s\n", $file, $ReplaceList;
$Keywords && printf L "KEYWORDS\t%s\t%s\n", $file, $Keywords;
if ($error < 0) {
return;
}
# On success, store the fields.
printf STF "MODE\t%s\t%s\t%s\t%s\t%s\n",
$file, $BinMode{$file},
$From{$file}, $RealHost{$file}, $UploadDate{$file};
printf STF "MAIL\t%s\t%s\t%s\n", $file, $Uploader, $Author;
printf STF "SHORT\t%s\t%s\n", $file, $Short;
$Source && printf STF "SOURCE\t%s\t%s\n", $file, $Source;
$ReplaceList && printf STF "REPLACE\t%s\t%s\n", $file, $ReplaceList;
$Keywords && printf STF "KEYWORDS\t%s\t%s\n", $file, $Keywords;
# Keyword Log
$Keywords && printf K "%s/%s\t%s\n", $RealCategory, $file, $Keywords;
# (Readable) File Status Log.
# List any Updates, Auto-rejects and Notes.
# Note: this is only performed for good files.
if (! -d "$PCWORK/$RealCategory") {
system ("mkdir $PCWORK/$RealCategory");
chmod 02770, "$PCWORK/$RealCategory";
}
$NCname = "$PCWORK/$RealCategory/00status";
open (NC, ">>$NCname") ||
printf STDERR "Cannot write '$NCname'\n";
printf N "OK\t %s\t%-8s\t%8s\t%s %s [ %s ]\n",
$CDString[$CdromStatus], $RealCategory, $file, $Title, $Keywords, $Source;
printf NC "OK\t %s\t%-8s\t%8s\t%s %s [ %s ]\n",
$CDString[$CdromStatus], $RealCategory, $file, $Title, $Keywords, $Source;
if ($ReplaceList) {
printf N "UPDATE\t%s\t%s\n", $RealCategory, $ReplaceList;
printf NC "UPDATE\t%s\t%s\n", $RealCategory, $ReplaceList;
printf U "UPDATE\t%s\t%s\n", $RealCategory, $ReplaceList;
}
close (NC);
} # write_keys
# -----------------------------------------------------------------------------
# Create new TXT or LSM file with long descriptions to use as source
# for WWW index files.
#
# Don't bother sorting index modules by category.
#
sub write_long_txt {
local ($cat, $mainfile, $txtfile) = @_;
local $n, $txtpath;
# $path = $INDEXDIR . "/" . get_category_path($cat);
$path = $INDEXDIR;
if (! -d $path) {
return $BAD_CATEGORY;
}
if ($txtfile) {
$txtpath = $path . "/" . $txtfile;
}
else {
($n, $foo) = split (/\./, $mainfile, 2);
$txtpath = $path . "/" . $n . ".txt";
}
# NOTE: 'textindex' and 'longindex.pl' expect .txt ot .lsm extensions only.
write_form ($txtpath);
chmod 0664, $txtpath;
return 0;
} # write_long_txt
sub write_form {
local ($txtpath, $foo) = @_;
if (!open (LI, ">$txtpath")) {
printf STDERR "Cannot write $txtpath\n";
printf L "ERROR\t$mainfile\tCannot write $txtpath\n";
return $BAD_FILE;
}
printf "Writing '$txtpath'\n";
# Write out the Standardized form
# These entries will build up the 00longindex.txt files
# If LSM file is given, copy the fields from it.
# WAVGL.ASC
# Title: Speech Research with WAVE-GL
# [ Category: mxlibs ]
# [ CDROM: no ]
# Keywords: DrDobbs Nov96 C++ AUDIO WAV
#
# [ free format description ]
#
if ($LSMmode) {
printf LI "Begin3\n";
}
# printf LI "Category: %s\n", $Category;
printf LI "Program: %s\n", $mainfile;
printf LI "Title: %s\n", $Title;
printf LI "Version: %s\n", $Version;
($LSMmode || $EnterDate) &&
printf LI "Entered-date: %s\n", $EnterDate;
printf LI "Description: %s\n", $Short;
printf LI "Keywords: %s\n", $Keywords;
# ---
# X2 Format additions
printf LI "Source-code: %s\n", $Source;
$FormatsList &&
printf LI "Formats: %s\n", $FormatsList;
# ---
printf LI "Author: %s\n", $Author;
if ($LSMmode) {
printf LI "Maintained-by: %s\n", $Uploader;
# printf LI "Primary-site: %s\n", "";
# printf LI "Alternate-site: %s\n", "";
# printf LI "Original-site: %s\n", "";
} # LSM Format
printf LI "Platforms: %s\n", $Platforms;
$Requires &&
printf LI "Requires: %s\n", $Requires;
printf LI "Copying-policy: %s\n", $CopyingPolicy;
# printf LI "Replaces: %s\n", $ReplaceList;
# printf LI "CDrom: %s\n", $Cdrom;
($LSMmode) &&
printf LI "End\n";
# Print Long Description
if (! $LSMmode) {
printf LI "\n---------------------------------------\n\n";
printf LI "%s\n", $LongDescription;
}
printf LI "\n";
close LI;
return 0;
} # write_form
# -----------------------------------------------------------------------------
# Scan given .txt, .txt*, .nfo. or .readme file for specific keywords.
# First the file given is searched for specific keywords. After that,
# further tests are performed for text in free format without any keywords.
#
# Return Values
# FILE_OK
# NO_TXTFILE
# DOC_BINARY
#
sub scan_txt {
local ($txtfile, $foo) = @_;
local $err = 0;
printf "\n"; # Begin new file
printf L "\n";
if (!$txtfile || !open (info, $txtfile)) {
printf STDERR "***Can't open input file '$txtfile'\n";
printf L "FAIL\t$mainfile\tRead failed for '$txtfile': File not found.\n";
printf L "SCAN\t$mainfile\tFatal: No records found.\n";
printf L "SCAN\t$mainfile\t*** Parser giving up.\n";
return $NO_TXTFILE;
}
line: while () {
### Clean up non-ASCII ###
if ($DosGraphics) {
tr/\012\015\032\200-\377//d; # Remove DOS graphics
} else {
tr/\012\015\032//d; # chop CR LF and EOF
}
### Protect the Scanner ###
# Note: the hexcode trap may accept some real words as well.
if (/^\s*$/) { next line; }
elsif (/\000\000+/ || # /\xFF\xFF/ ||
# /^\s*[0-9A-Fa-f]{10,}\s*$/ ||
/^\s*\{\\rtf/i || # RTF file (.DOC)
/WordDocument|W.o.r.d.D.o.c.u.m.e.n.t/) { # WORD file (.DOC)
printf "*** SCANNER ABORTED ***\n";
printf L "SCAN\t%s\t[%s] Line %2d\t*** SCANNER ABORTED *** <<%s>>",
$mainfile, $txtfile, $., $_; # '$.' is linecount
return $DOC_BINARY;
}
# awe_help.doc
#
# 0000: d0 cf 11 e0 a1 b1 1a e1 00 00 00 00 00 00 00 00 ................
# 0010: 00 00 00 00 00 00 00 00 3e 00 03 00 fe ff 09 00 ........>.......
# 0020: 06 00 00 00 00 00 00 00 00 00 00 00 06 00 00 00 ................
# 0030: 00 00 00 00 00 00 00 00 00 10 00 00 78 00 00 00 ............x...
# 0040: 01 00 00 00 fe ff ff ff 00 00 00 00 01 00 00 00 ................
# 0050: 85 00 00 00 03 00 00 00 83 00 00 00 fb 00 00 00 ................
# 0060: 22 02 00 00 ff ff ff ff ff ff ff ff ff ff ff ff "...............
# 0070: ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ff ................
# ...
# 0200: 52 00 6f 00 6f 00 74 00 20 00 45 00 6e 00 74 00 R.o.o.t. .E.n.t.
# 0210: 72 00 79 00 00 00 00 00 7e 06 00 00 1c 00 00 00 r.y.....~.......
# ...
# 04A0: 00 00 2f 60 2c 6b 01 20 03 61 6c 01 71 00 00 00 ../`,k. .al.q...
# 04B0: 00 05 00 00 00 00 00 57 00 6f 00 72 00 64 00 44 .......W.o.r.d.D
# 04C0: 00 6f 00 63 00 75 00 6d 00 65 00 6e 00 74 00 00 .o.c.u.m.e.n.t..
# 04D0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 ................
#
# wgt/prv2_2.zip/pr/doc/prman.doc
#
#{\rtf1\ansi\ansicpg1252\uc1 \deff11\deflang1033\deflangfe1033{\fonttbl{\f0\froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman;}{\f1\fswiss\fcharset0\fprq2{\*\panose 020b0604020202020204}Arial;}
#
### Scan for Required fields ###
# Short: or ^Summary: (news header)
elsif (/^\s*\S?\s*Short\s*:\s*(.+)/i || /^Summary\s*:\s*(\S+)/) {
$Short = $1;
next line;
}
# Category: or Type:
# Incorrect values like Shareware, Demo, etc are checked later.
elsif (/^\s*Type\s*:\s*(.+)/i || /^\s*Cat[ae][cg]ory\s*:\s*(.+)/i) {
$Category = $1;
$Category =~ tr/A-Z/a-z/;
if ($Category =~ /(\S+[\/\\]\S+)/) { # Weed out extra text
$Category = $1;
}
###
print L "TYPE\t$mainfile\t$Category\n";
if (/\s*Cata[cg]ory/i) {
printf L "SCAN\t$mainfile\tMalformed keyword, supposing 'Category'\n";
printf STDERR "***SCAN\t$mainfile\tMalformed keyword, supposing 'Category'\n";
}
next line;
}
# CD-ROM -- CDrom Permission: (not case sensitive)
# Check for sub-packages having different CD_ROM statuses.
elsif (/^\s*\S?\s*CD[\-\ ]*ROM\s+Permission\s*:\s*(.+)\.*\s*$/i ||
/^\s*\S?\s*CD[\-\ ]*ROM\s*:\s*(.+)\.*\s*$/i ||
(/CD[\-\ ]*ROM/i && /permission/i && /\:\s*(.+)\.*\s*$/)) {
($Cdrom = $1) =~ tr/A-Z/a-z/;
if ($CdromStatus != $CD_NONE) {
printf "$mainfile:\tMultiple CD-ROM lines encountered.\n";
printf L "SCAN\t$mainfile\tMultiple CD-ROM lines encountered '$1'.\n";
}
if ($Cdrom =~ /^yes/i || $Cdrom =~ /^sure/i) {
$CdromStatus = $CD_YES;
}
elsif ($Cdrom =~ /^no/i || $Cdrom =~ /forbid/i) {
$CdromStatus = $CD_NO;
}
elsif ($Cdrom =~ /prior|^written|require[ds]/i) {
$CdromStatus = $CD_PERM;
}
else {
print L "SCAN\t$mainfile\tUnknown or malformed CD-ROM status '$Cdrom'\n";
printf STDERR "***SCAN\t$mainfile\tUnknown or malformed CD-ROM status '$Cdrom'\n";
}
next line;
}
### Optional fields ###
# Accept decorated formatting (? = Match 1 or 0 times).
# elsif (/^\s*\S?\s*Filenames*\s*:\s*\(*\s*(.+)\s*\)*/i) {
# $ = $1;
# next line;
# }
elsif (/^\s*\S?\s*Part\s*:\s*(.+)/i) {
$PartNumber = $1;
next line;
}
elsif (/^\s*\S?\s*Repla[cs]es*\s*:\s*(.+)/i) {
if (! ($1 =~ /^no\.*$|^none|^nothing\.?$/i)) { # Weed out 'no files'
$ReplaceList .= $1 . " ";
}
next line;
}
elsif (/^\s*\S?\s*Sour*[cs]e\s*:\s*(.+)/i ||
/^\s*\S?\s*Sour*[cs]e[\-\s]*code\s*:\s*(.+)/i) {
$Source = $1;
if ($Source =~ /^no\.*$|^none/i) {
$Source = "No src";
}
next line;
}
elsif (/^\s*\S?\s*Formats?\s*:\s*(.+)/i) {
$FormatsList = $1;
next line;
}
#
# 'Creator(s):' could be either the Author or some generator program.
# But that's to be worried about later...
elsif (/^\s*\S?\s*Creator\(?s?\)?\s*:\s*(.+)\s*/i) {
$Creator = $1;
next line;
}
#
# -------------------------------------------------------------------
#
### Linux Software Map (LSM) fields ###
### Author(s) (not case sensitive). Multiple lines should be alloved.
# 'Address:' may conflict with order/register forms.
elsif (/^\s*\S?\s*Author\(?s?\)?\s*:\s*(.+)\s*/i ||
/^\s*\S?\s*Written\s+by\s*:?\s*(.+)\s*/i ||
/^\s*\S?\s*Contact\s*:\s*(.+)\s*/i ||
/^\s*\S?\s*Contact\s*Address\s*:\s*(.+)\s*/i ||
/^\s*\S?\s*E\-*mail\s*:?\s*(\S+)\s*/i ||
/\s+E\-*mail\s*:?\s*(\S+)\s*/i) {
$tmp = $1;
if ($tmp =~ /^-+|^_+/) {
++$IF_Has_register;
++$IF_Commercial;
next;
}
if ($Author) { # Empty expected
printf "$mainfile:\tMultiple Author lines encountered.\n";
printf L "SCAN\t$mainfile\tMultiple Author lines encountered '$tmp'.\n";
}
# $Author .= &ParseAddress($tmp) . " ";
$Author .= $tmp . " ";
next line;
}
# Uploader: or ^From: (news header)
elsif (/^\s*Uploader\s*:\s*(\S+)/i || /^From:\s*(\S+)/) {
if ($Uploader) { # Empty expected
printf "$mainfile:\tMultiple Uploader lines encountered.\n";
printf L "SCAN\t$mainfile\tMultiple Uploader lines encountered '$1'.\n";
}
$Uploader = &ParseAddress($1);
next line;
}
elsif (/^Begin3$/) {
++$LSMmode; # Flag on Linux Software Map mode.
next line;
}
elsif (/^End$/ && $LSMmode) {
$LSMmode = -1; # Flag done Linux Software Map mode.
next line;
}
#
# 'Name/nome'
# 'Company/companhia'
# 'Program/Programa'
# 'Category/Categoria'
# 'Author/Autor'
# 'Requirements/Requerimentos'
#
elsif (/^\s*\S?\s*Title\s*:\s*(.+)/i ||
/^\s*\S?\s*Program\s*Name\s*:\s*(.+)/i) {
$Title = $1;
next line;
}
elsif (/^\s*\S?\s*File\s*:\s*(.+)/i ||
/^\s*\S?\s*Program\s*:\s*(.+)/i) {
if (! $Title) {
$Title = $1;
}
next line;
}
elsif (/^\s*\S?\s*Version\s*Number\s*:\s*V*\.*\s*(.+)/i ||
/^\s*\S?\s*Version\s*:?\s*V*\.*\s*(.+)/i) {
$Version = " V " . $1;
next line;
}
# For date, expect "year-mm-dd", "dd mm year", or "dd Mon(th) year".
elsif (/^\s*\S?\s*Entered[\-\ ]Date\s*:\s*(.+)/i) {
$EnterDate = $1;
next line;
}
elsif (/^\s*\S?\s*Description\s*:\s*(.+)/i ||
/^\s*Content[\-\ ]Description\s*:\s*(.+)/i) {
$Short = $1;
next line;
}
elsif (/^\s*\S?\s*Keywords?\s*:\s*(.+)/i) {
$Keywords = $1;
next line;
}
elsif (/^\s*\S?\s*Maintained[\-\ ]By\s*:\s*(.+)/i) {
$Uploader = &ParseAddress($1);
next line;
}
# LSM Addresses
elsif (/^\s*\S?\s*Primary[\-\ ][Ss]ites?\s*:\s*(\S+)/ ||
/^\s*\S?\s*Alternate[\-\ ][Ss]ites?\s*:\s*(\S+)/) {
($site = $1) =~ tr/A-Z/a-z/;
if (!$Category && ($site =~ /^x2ftp/)) {
($Category = $site) =~ /(msdos[\/\\]\S+)/;
printf STDERR "***DEBUG: LSM: got Category = $Category\n";
printf L "DEBUG: LSM: got Category = $Category\n";
} # x2ftp
next line;
}
elsif (/^\s*\S?\s*Plata?forms?\s*:\s*(.+)/i) {
$Platforms = $1;
next line;
}
elsif (/^\s*\S?\s*Copying[\-\ ]Policy\s*:\s*(.+)/i) {
$CopyingPolicy = $1;
next line;
}
# 'Company'
# 'Name'
# 'Address'
# 'Phone'
# 'Fax'
# 'Total'
# 'Empfaenger'
# 'Konto-Nummer'
# 'Bankleitzahl'
# 'Name'
# 'Address'
#
# 'NAME'
# 'STREET'
# 'STATE'
# 'TELEPHONE'
# 'Signature'
# Bank connection:
# account number:
# Order or Registering form ...
elsif (/^\s*\S?\s*Registration\s*Form/i ||/^\s*\S?\s*Registration\s*key/i ||
/Users? Licence/i ||
/^\s*\S?\s*Payment\s*:\s*(.+)/i ||
/^\s*\S?\s*Name\s*:\s*(.+)/i || # Beware of program name
/^\s*\S?\s*Phone\s*#?\s*:\s*(.+)/i ||
/^\s*\S?\s*Telephone\s*#?\s*:\s*(.+)/i ||
/^\s*\S?\s*Fax\s*#?\s*:\s*(.+)/i ||
/^\s*\S?\s*Company\s*:\s*(.+)/i ||
# /^\s*\S?\s*Address\s*:\s*(.+)/i) || # May be confused with Author
/^\s*\S?\s*Street\s*:\s*(.+)/i ||
/^\s*\S?\s*City\s*:\s*(.+)/i ||
/^\s*\S?\s*City[,\/\s]State\s*:\s*(.+)/i ||
/^\s*\S?\s*State\s*:\s*(.+)/i ||
/^\s*\S?\s*State[,\/]*\s*ZIP\s*:\s*(.+)/i ||
/^\s*\S?\s*State[,\/]*\s*ZIP\s*Code\s*:\s*(.+)/i ||
/^\s*\S?\s*Signature\s*:\s*(.+)/i ||
/^\s*\S?\s*Country\s*:\s*(.+)/i) {
++$IF_Has_register;
++$IF_Commercial;
next line;
}
print ">> $_\n";
# Mail Headers
if (/^Subject:\s+(.+)\s*/) {
$Title = $1;
next line;
}
elsif (/^\s*\S?\s*Date\s*:\s+(.+)\s*/i) {
$EnterDate = $1;
next line;
}
elsif (/^To:\s+|^Content-[Tt]ype:\s+/) {
next line;
}
### Collect the Long Description.
$LongDescription .= " $_";
++$LineCount;
### Search some keywords in the text ...
# FTP and WWW Addresses
# NOTE: Beware of "other references" being interpreted as Category
# declaration.
if (/ftp:\/\/(\S+)/i || /http:\/\/(\S+)/i) {
($site = $1) =~ tr/A-Z/a-z/;
if (!$Category && ($site =~ /^x2ftp/)) {
($Category = $site) =~ /(msdos[\/\\]\S+)/;
printf STDERR "***DEBUG: LINK: got Category = $Category\n";
printf L "DEBUG: LINK: got Category = $Category\n";
} # x2ftp
next line;
}
if (! $Category) {
if (/(msdos[\/\\]programming[\/\\]\S+)/i) {
$Category = $1;
$Category =~ tr/A-Z/a-z/;
printf STDERR "***DEBUG: FREE: got Category = $Category\n";
printf L "DEBUG: FREE: got Category = $Category\n";
}
} # Category
if (/Copyright\s+(.+)/i || /^\(C\)\s+(.+)/i || /\s+\(C\)\s+(.+)/i) {
if (! $Author) {
$Author = $1;
}
if (! $CopyingPolicy) {
$CopyingPolicy .= "$_\n";
}
} # Copyright
# /(\S+) inc/i || /all rights reserved/i
### Copying Policy
# GPL, BSD, Public Domain, Freeware, Cardware, Shareware, Copyright
# register, fee, payment
if (/[\$S]hareware/i) {
++$IF_Shareware;
}
# Ignore
if (/^\s*Options\s*:/i ||
/^\s*Notes?\s*:/i ||
/^\s*Usage\s*:/i ||
/^\s*Appendix\s*:/i) {
next;
}
# Flag Errors
if (/^\s*(\S\S+)\s*:\s*(\S+)/) {
printf STDERR "***SCAN: [%s]\tUnknown or malformed keyword '$1'\n\n",
$txtfile;
printf L "SCAN\t%s\t[%s:%2d]\tUnknown or malformed keyword '$1'\n",
$mainfile, $txtfile, $.; # '$.' is linecount
}
} # while
# -------------------------------------------------------------------------
# Final Cleaning. Clear variables that only contain whitespace.
#
if ($Short =~ /^\s*$/) {
$Short = "";
}
if ($Source =~ /^\s*$/) {
$Source = "";
}
if ($ReplaceList =~ /^\s*$/) {
$ReplaceList = "";
}
# Shrink spaces. Beware of things like 'C++', 'C--', 'http://', etc.
printf STDERR "\nShrinking text...\n";
# Reduce: 2 or more Spaces, .... --- ~~ __ == || /// :: ** $$ !!
$LongDescription =~ s/\s\s+|\.\.\.\.+|\-\-\-+|\~\~+|__+|==+|\|\|+|\/\/\/+|::+|\*\*+|\$\$+\!\!+/\ /gs;
# Reduse: [ _ ~ * = | \ < > ] with any of [ + - _ ~ * = / | \ ! ]
$LongDescription =~ s/[\_\~\*\=\|\\\<\>]\s*[\+\-\_\~\*\=\/\|\\\!]/\ /gs;
$LongDescription =~ s/[\_\~\*\=\|\\\<\>]\s*[\+\-\_\~\*\=\/\|\\\!]/\ /gs;
$LongDescription =~ s/[\+\-\_\~\*\=\/\|\\\<\>]\s+[\+\-\_\*\=\/\|\\]/\ /gs;
$LongDescription =~ s/\s\s+|\s+\S(\s+\S)+\s+/\ /gs;
#
# ÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄÄ
# $$$$$$$$g$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$g$$$$$$$$$$$$$
# $$$$$$$' `$$' `$$' `$$' $$$$ $$' `$$ `$' `$$$$$$$
# $$$$$$$ $$$$$$ $$$$$$$$ $$$$ $$ $$ $$ $$ $ $$$$$$$
# $$$$$$$ $ $$ $$$$$$ $$$$ $. $ $$ $$ $$ $ $$$$$$$
# $$$$$$$ $$ $$ $$$$$$$$ $$$$ $$$$ $$ $$ $$ $ $$$$$$$
# $$$$$$$, .$$, .$$$$ $$$$, $$$$ $$ $$ ,$ $$$$$$$
# $$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$$
#
# squeezed result:
#
# getmid15.zip g g ' ` ' ` ' ` ' ' ` `$' ` $ $ $. $ $ $ , . , . , ,$
if (!$Short) {
if ($Title) {
$Short = $Title . $Version;
}
else {
# Cut Long Description to the max length of Short Description
print L "SCAN\t$mainfile\tScan failed for '$txtfile': 'Short' record not found.\n";
$Short = substr($LongDescription, 0, $MaxDescLen);
}
} # Short
if (!$Uploader) {
print L "SCAN\t$mainfile\tScan failed for '$txtfile': 'Uploader' record not found.\n";
}
#
# These are not the same thing by definition.
# E.g. 'Creator' could be just anything.
#
if (!$Author && $Creator) {
$Author = $Creator;
}
if ($Author && $Uploader =~ /Author/i) {
$Uploader = $Author; # Not equal by default
}
if (!$Cdrom) {
print L "SCAN\t$mainfile\tScan failed for '$txtfile': 'CDrom' record not found.\n";
}
close info;
printf STDERR "Done Scan [$txtfile].\n\n";
return $FILE_OK;
} # scan_txt
# -----------------------------------------------------------------------------
#
# Parse Values
#
# -----------------------------------------------------------------------------
# Try to figure out all possible ways to identify oneself.
# Email address or LSM entry is expected, though.
#
sub ParseAddress {
# local($n1, $n2) = @_;
local $n1 = $_;
# Formats:
# addr@host Valid formats
# addr@host (Real Name)
# Real.Name@host (Real Name)
# Real Name
# Real Name
# Real Name Invalid format
#
# Get address
if (/\<*([A-Za-z0-9\.\_\-]+\@[A-Za-z0-9\.\_\-]+)\>*/) {
$email = $1;
}
# Get realname
if (/\(*(\S+[\.\s+].+)\)*\s+\<*/) {
$name = $1;
}
if (! $email) {
printf "***No e-mail address found.\n";
}
# `validmail $1`;
return ($email);
}
# -----------------------------------------------------------------------------
#
sub check_replace {
local ($file, $foo) = @_;
local $path = "";
if (! $ReplaceList && $err == $KEYSMISSING) {
# Try to locate any old version.
$hum = $1 . ".*";
while(<${hum}>) {
if ($file ne $_) {
$rmlist .= "$_ ";
}
}
}
else { # Split ReplaceList line
# split (
# foreach @tmp;
# Check path, add if none given
# /(\S+)[\/\\]([0-9A-Za-z_\-]+)/
if (! $path) {
$path = $RealCategory;
}
}
return $rmlist;
} # check_replace
# -----------------------------------------------------------------------------
# Return full pathname to FTP category.
# Note: CD-ROM routines use check_cdrom_category instead.
sub get_category_path {
local ($cat, $foo) = @_;
if ($cat =~ /arcers|virus/) { return $cat; }
elsif ($cat =~ /programming/) { return $cat; }
return "programming/$cat";
} # get_category_path
# -----------------------------------------------------------------------------
#
# Checking
#
# -----------------------------------------------------------------------------
sub check_records {
local $err = 0;
if (!$mainfile) {
printf STDERR "\n***No mainfile for '$txtfile'. Skipping...\n";
return $NO_MAINFILE;
}
# Check that at least the required fields exist.
if (!$Short || !$Category || !$Uploader || !$CdromStatus) {
print L "SCAN\t$mainfile\tWarning: '$mainfile': Required fields missing.\n";
print L "FAIL\t$mainfile\tCannot move file.\n";
$err = $KEYSMISSING;
}
# Type or Category. First check if the value is not directory as expected.
$err = check_dos_category($Category);
$RealCategory = $cat; # Set to 'none' upon error.
if ($err == $BAD_CATEGORY) {
# if ($Category =~ /[\$S]hareware/i) {
# }
$Category = ""; # Clear incorrect value.
} # Bad Category Value
### Joillekin tiedostoille Category menee kokonaan tyhjaksi, vaikka
### pitaisi olla "none".
if (!$RealCategory && !$Category) {
print L "SCAN\t$mainfile\tScan failed for '$txtfile': 'Category' record not found.\n";
# No Category given. Do some thinking...
if ($Keywords) {
$Category = "none";
if ($Keywords =~ /audio/) { $Category = "mxcode"; }
elsif ($Keywords =~ /source/) {
if ($Keywords =~ /game/) { $Category = "gamesrc"; }
elsif ($Keywords =~ /demo/) { $Category = "demosrc"; }
else { $Category = "source"; }
} # source
$RealCategory = $Category;
} # Keywords
else {
$Category = "none";
$RealCategory = "none";
} # !Keywords
}
return $err;
}
# -----------------------------------------------------------------------------
#
# Check given Category is valid ...
# 'CHECKING' line starts a new file on the screen output.
#
# Returns the category in $cat
#
# Return Values
# FILE_OK
# NO_CATEGORY
# BAD_CATEGORY
#
sub check_dos_category {
local $err = $NO_CATEGORY;
$cat = "none"; # No Category
# Split the Category path.
$p[0] = $p[1] = $p[2] = $p[3] = "";
($path, $foo) = @_;
if (!$path) { # Still failed
print L "FAIL\t$mainfile\tNo Category specified.\n";
print "No Category specified for '$mainfile'.\n";
$path = "programming/unknown";
$cat = "none"; # No Category
$err = $NO_CATEGORY;
}
else {
@p = split (/\/|\\/, $path); # accept both / and \
(!$p[0]) && shift @p;
($p[0] eq "pub") && shift @p;
($p[0] eq "programming") && shift @p; # path in wrong order...
($p[0] eq "msdos") && shift @p;
# Remove possible filename from the path.
if ($p[2]) { $p[2] = ""; }
elsif (!$p[2] && $p[1]) { $p[1] = ""; }
# Check given Category is valid ...
if ($p[0] =~ /^arcers?|^archivers?|^packers?/ && !$p[2]) {
$cat = "arcers";
$err = $FILE_OK;
}
elsif (!$p[2] && ($p[0] eq "virus" || $p[0] eq "transfer")) {
$cat = $p[0];
$err = $FILE_OK;
}
# It must be programming/something. Try both ways...
# ignore missing "programming".
elsif ($p[0] eq "programming" && $p[1] && !$p[2]) {
$err = check_prg_category($p[1]); # Sets $cat
}
elsif ( !$p[1] && !$p[2]) {
$err = check_prg_category($p[0]);
}
else {
$err = $BAD_CATEGORY;
}
# Set $cat on success, else print error message.
if ($err != $FILE_OK) {
print L "FAIL\t$mainfile\tInvalid directory path '$path'\n";
print "Invalid Category '$path'\n";
# print ">>>'$p[0]'\t'$p[1]'\t'$p[2]'\n";
$cat = "none"; # Not valid Category
}
} # path
return $err;
}
sub check_prg_category {
local ($_, $foo) = @_;
$cat = "none"; # Default
if (!$_) { # Nothing given
# $Category = "programming/unknown";
# print L "FAIL\t$mainfile\tNo Category specified.\n";
printf ("\n***No Category specified for '%s'.\n", $mainfile);
return $NO_CATEGORY;
}
printf "\nChecking Category '$_'\n";
if (/^3dfx$/) { $cat = "3dfx"; }
elsif (/^ack$/) { $cat = "ack"; }
elsif (/^ai$/) { $cat = "ai"; }
elsif (/^arts*$/) { $cat = "art"; }
elsif (/^biz$/) { $cat = "biz"; }
# books # Separate ftp area
elsif (/^compress$|^pack$/) { $cat = "compress"; }
elsif (/^contest\d*$/) { $cat = "contest"; }
elsif (/^convert$/) { $cat = "convert"; }
elsif (/^creative$/ ) { $cat = "creative"; }
elsif (/^crypt$/) { $cat = "crypt"; }
elsif (/^demosrcs?$/) { $cat = "demosrc"; }
elsif (/^directx$/) { $cat = "directx"; }
elsif (/^djgpp$/) { $cat = "djgpp"; } # No uploads on djgpp
elsif (/^djgpp2$/) { $cat = "djgpp2"; }
elsif (/^docs?$|^documents?$/){ $cat = "docs"; }
elsif (/^faqs?$/) { $cat = "faq"; }
elsif (/^fg$/) { $cat = "fg"; }
elsif (/^formats?$/) { $cat = "formats"; }
elsif (/^gamesrcs?$/) { $cat = "gamesrc"; }
elsif (/^gdm$/) { $cat = "gdm"; }
elsif (/^ghm$/) { $cat = "ghm"; }
elsif (/^gpe$/) { $cat = "gpe"; }
elsif (/^hardware$/) { $cat = "hardware"; }
elsif (/^iguana$/) { $cat = "iguana"; }
elsif (/^indeo$/) { $cat = "indeo"; }
elsif (/^langs?$/) { $cat = "lang"; }
elsif (/^libs?$/) { $cat = "libs"; }
elsif (/^math$/) { $cat = "math"; }
elsif (/^memory$/) { $cat = "memory"; }
elsif (/^microsoft$/) { $cat = "microsoft"; }
elsif (/^misc$/) { $cat = "misc"; }
elsif (/^mxcode$/) { $cat = "mxcode"; }
elsif (/^mxdata$/) { $cat = "mxdata"; }
elsif (/^mxinfo$/) { $cat = "mxinfo"; }
elsif (/^mxlibs?$/) { $cat = "mxlibs"; }
elsif (/^mxutils?$/) { $cat = "mxutil"; }
elsif (/^net$/) { $cat = "net"; }
elsif (/^news$/) { $cat = "news"; }
elsif (/^ng$/) { $cat = "ng"; }
elsif (/^pcme$/) { $cat = "pcme"; }
elsif (/^pmode$/) { $cat = "pmode"; }
elsif (/^png$/) { $cat = "png"; }
elsif (/^progsrcs?$/) { $cat = "progsrc"; }
elsif (/^qualitas$/) { $cat = "qualitas"; }
elsif (/^reviews?$/) { $cat = "reviews"; }
elsif (/^rules?$/) { $cat = "rules"; }
elsif (/^scitech$/) { $cat = "scitech"; }
elsif (/^serial$/) { $cat = "serial"; }
elsif (/^srcs?$|^sources?$/){ $cat = "source"; }
elsif (/^specs?$/) { $cat = "specs"; }
elsif (/^theory$/) { $cat = "theory"; }
elsif (/^utils?$/) { $cat = "utils"; }
elsif (/^vla$/) { $cat = "vla"; }
elsif (/^watcom$/) { $cat = "watcom"; }
elsif (/^wgt$/) { $cat = "wgt"; }
elsif (/^windows$/) { $cat = "windows"; }
elsif (/^wnt$/) { $cat = "wnt"; }
elsif (/^www$/) { $cat = "www"; }
elsif (/^x2info$/) { $cat = "x2info"; }
elsif (/^x2www$/) { $cat = "x2www"; }
elsif (/^xlibs?$/) { $cat = "xlib"; }
elsif (/^xwindows?$/) { $cat = "xwindow"; }
#
elsif (/^notxt$/) { $cat = "notxt"; }
else {
print "*** Invalid Category '$_'\n";
return $BAD_CATEGORY;
} # spelling
return $FILE_OK;
}
# -----------------------------------------------------------------------------
#
## Dissolve
#
# -----------------------------------------------------------------------------
#
# Dissolve the archive under PCWORK
#
# Note: ERROR records can be created here as well, even though the files
# passed ziptest. In that case, "archive format error" will be reported.
#
# This routine also writes out STATUSFILE to be used when moving the files
# onto place.
#
# Only process archives with good TXT.
#
#
# Return Values
# FILE_OK
# BAD_FILETYPE
# NO_CATEGORY
# MAIN_IS_TXT
#
# Dissolve an archive by Category
sub dissolve {
$ZL = 0;
$ds_err = E_NONE;
$DisCount = 0; # Count files in the archive.
$Deferred = ""; # Deferred 'Archive:' line.
if (!$RealCategory) { # Error ?
printf STDERR "\n\nFAIL\t%9s\tNo category given. Dissolving skipped.\n",
$mainfile;
printf L "FAIL\t%9s\tNo category given. Dissolving skipped.\n",
$mainfile;
return $NO_CATEGORY;
}
print "\nCategory '$RealCategory' $mainfile\n";
# Create 'Category' main directory
$destdir = $PCWORK . "/" . $RealCategory;
if (! -d $destdir) {
system ("mkdir $destdir");
chmod 02770, $destdir;
}
# Write STATUSFILE entry (for Move)
printf STF "CHECK\t%d\t$RealCategory\t$mainfile\t$txtfile\n", $CdromStatus;
# $RealCategory on korjattu kategoria.
# Update the Index file
open (IX, ">> $destdir/00add.txt");
printf IX "%s\t%s\t%s\n", $mainfile, $Short, $Source;
close IX;
# .TXT file itself is the mainfile
if ($mainfile =~ /(\.txt|\.doc|\.diz|\.faq|\.nfo|\.readme)/i) {
system ("cp $mainfile $destdir");
chmod 0660, "$destdir/$mainfile";
++$IF_Docs;
printf STDERR "\n*** Warning: mainfile is a txt file.\n";
return $MAIN_IS_TXT;
}
elsif ($txtfile) {
system ("cp $txtfile $destdir"); # move the .txt file as well
chmod 0660, "$destdir/$txtfile";
}
# Finally, process the Mainfile
# Create subdir for each archive, and add its name to the path.
$zipcomm = "";
$ds_err = dissolve_file("$destdir/$mainfile", $cwd, $mainfile);
#
# Summary of the archive.
$CommentFileName = "$mainfile";
$CommentFileName =~ tr/\./_/; # Remove DOS-offending markers
$CommentFileName = "$destdir/" . "$CommentFileName" . ".rem";
if ($zipcomm) { # Did it catch anything ?
if ($GlobalCommentFile) {
print GLOBAL_CMT $zipcomm;
}
open (CMT, ">$CommentFileName");
print CMT $zipcomm;
close (CMT);
chmod 0660, "$CommentFileName";
}
# Flag file types included in the archives
if ($EvaluateFiletypes) {
# Claimed yes, but contents seem suspicious
if ($IF_Commercial && $CdromStatus != $CD_NO) {
$CdromStatus = $CD_CONFL;
printf STDERR "\n*** CD-ROM Status Conflicts.\n"; # CD Conflict
printf L "ERROR\t$mainfile\t*** CD-ROM Status Conflicts.\n";
printf N "ERROR\t$mainfile\t*** CD-ROM Status Conflicts.\n";
}
printf STF "CONTENT\t%s\t%3s %2s %2s %2s %2s %2s %2s %2s %2s %2s %2s %2s %2s %2s %2s %2s %2s %2s %2s\n",
$mainfile, $DisCount,
$IF_Readme, $IF_Docs, $IF_Miscinfo,
$IF_Source, $IF_Exec, $IF_Config, $IF_Object,
$IF_Winapp, $IF_Install,
$IF_Data, $IF_Data_audio, $IF_Data_image, $IF_Archive,
$IF_System, $IF_UnknownType,
$IF_Shareware, $IF_Commercial, $IF_Has_register;
# Flag file types included
# Write the contents description in plain text to the .rem file.
# display content
{
if ($GlobalCommentFile) {
DumpInfoSummary(GLOBAL_CMT, $destdir, $mainfile);
}
open (CMT, ">>$CommentFileName");
DumpInfoSummary(CMT, $destdir, $mainfile);
close (CMT);
chmod 0660, "$CommentFileName";
} # display_content
} # evaluate
return $ds_err; # return last error
} # dissolve
sub DumpInfoSummary {
local ($CMT, $destdir, $mainfile) = @_;
printf $CMT "\n$mainfile\n\n";
printf $CMT "Category: $destdir\n";
printf $CMT "Short: %s\n", $Short;
printf $CMT "Source: %s\n", $Source;
printf $CMT "Keywords: %s\n", $Keywords;
printf $CMT "CD-ROM: %s\n", $Cdrom;
printf $CMT "\n";
printf $CMT " %2d readme files.\n", $IF_Readme;
printf $CMT " %2d document files.\t\t%s\n", $IF_Docs, $DocsList;
$IF_Miscinfo &&
printf $CMT " %2d misc info files.\n", $IF_Miscinfo;
printf $CMT " %2d program source files.\t%s\n", $IF_Source, $SourceList;
printf $CMT " %2d executable program files.\n", $IF_Exec;
$IF_Config &&
printf $CMT " %2d config data files.\n", $IF_Config;
$IF_Object &&
printf $CMT " %2d compiler object files.\n", $IF_Object;
$IF_Winapp &&
printf $CMT " %2d Windows application files.\n", $IF_Winapp;
$IF_Install &&
printf $CMT " %2d Install datafiles.\n", $IF_Install;
$IF_Data &&
printf $CMT " %2d data files [ %2d audio %2d image].\n",
$IF_Data, $IF_Data_audio, $IF_Data_image;
$IF_Archive &&
printf $CMT " %2d archive files.\n", $IF_Archive;
$IF_System &&
printf $CMT " %2d system files.\n", $IF_System;
$IF_UnknownType &&
printf $CMT " %2d unknown type files.\n", $IF_UnknownType;
printf $CMT "\n";
printf $CMT " %3d files total.\n\n", $DisCount;
printf $CMT " %2d shareware.\n", $IF_Shareware;
printf $CMT " %2d commercial.\n", $IF_Commercial;
$IF_Has_register &&
printf $CMT " %2d registering.\n", $IF_Has_register;
$CopyingPolicy &&
printf $CMT " Copyright: %s\n", $CopyingPolicy;
$ReplaceList && printf $CMT "\n file is update.\n";
printf $CMT "\n";
printf $CMT "----\n\n"; # file separator
}
sub dissolve_file {
local ($destdir, $srcdir, $mainfile) = @_;
local $cmd;
local $err = $FILE_OK;
# Subdirectory for Mainfile
if (! -d $destdir) {
system ("mkdir $destdir");
chmod 02770, $destdir;
}
if (! -f "$srcdir/$mainfile") {
printf "*** Mainfile not found: '$srcdir/$mainfile'\n\n";
printf L "FAIL\t$destdir/$mainfile\tFile not found\n";
return ($NO_MAINFILE);
}
if ($mainfile =~ /(\.asm|\.bas|\.cc?|\.cpp|\.hh?|\.hpp|\.pas)/) {
$SourceList .= "$1 "; # Sort these out later...
++$IF_Source;
print " copy: $destdir/$mainfile\n";
system ("cp $mainfile $destdir");
# chmod 0660, "$destdir/$mainfile";
}
elsif ($mainfile =~ /(\.txt|\.doc|\.e?ps|\.faq|\.man|\.htm|\.swg)/i) {
$DocsList .= "$1 "; # Sort these out later...
++$IF_Docs;
print " copy: $destdir/$mainfile\n";
system ("cp $mainfile $destdir");
# chmod 0660, "$destdir/$mainfile";
}
elsif ($mainfile =~ /\.exe|\.com/i) {
++$IF_Exec;
print " copy: $destdir/$mainfile\n";
system ("cp $mainfile $destdir");
# chmod 0660, "$destdir/$mainfile";
}
else {
if ($mainfile =~ /\.arj|\.ARJ/) {
$cmd = "cd $destdir ; $UNARJ $srcdir/$mainfile";
}
elsif ($mainfile =~ /\.arc|\.ARC/) {
$cmd = "cd $destdir ; $UNARC $srcdir/$mainfile";
}
elsif ($mainfile =~ /\.lha|\.LHA|\.lzh|\.LZH/) {
$cmd = "$UNLZH xw=$destdir $mainfile";
# $cmd = "cd $destdir ; $UNLZH $srcdir/$mainfile";
}
elsif ($mainfile =~ /\.zip|\.ZIP/) {
$cmd = "$UNZIP $srcdir/$mainfile -d $destdir";
}
elsif ($mainfile =~ /\.zoo|\.ZOO/) {
$cmd = "cd $destdir ; $UNZOO $srcdir/$mainfile";
}
# UNRAR not available
elsif ($mainfile =~ /\.rar|\.RAR/) {
# $cmd = "cd $destdir ; $UNRAR $srcdir/$mainfile";
printf L "FAIL\t$destdir/$mainfile\tInvalid filetype: $mainfile\n\n";
printf N "FAIL\tPlease, use $UNRAR for: %s\n", $mainfile;
return $BAD_FILETYPE;
}
else {
printf STDERR "\n*** Invalid filetype: $mainfile\n";
printf STDERR "*** FILE SKIPPED: $mainfile\n\n";
printf L "FAIL\t$destdir/$mainfile\tInvalid filetype: $mainfile\n\n";
return $BAD_FILETYPE;
}
# system ($cmd);
$Z = "Z_$ZL";
open ($Z, "$cmd |");
while (<$Z>) {
$Verbose && print "$_";
chop;
if (/^\s*Archive:\s+/) { # Don't print the 'Archive:' line alone.
if ($Deferred) {
$zipcomm .= "$Deferred\n";
}
$Deferred = $_;
next;
}
elsif (/^\s*creating:\s+/) { next; }
# Extract Files
# --------------------------------------------------------
# arc ^Extracting file: (file)$
# unarj ^Extracting (FILE) ... Binary file! ...
# unzip ^ inflating: (file)$
# unzip ^ exploding: (file)$
# unzip ^ extracting: (file)$
# unzip ^unshrinking: (file)$
# zoo ^Zoo: (file) -- extracted$
# lha ^(file)- Melted $
#
if (/^\s*(inflating:|exploding:|unshrinking:|[Ee]xtracting\s+file:|[Ee]xtracting:?)\s+(\S+)/ ||
/^\s*(Zoo:)\s+(\S+)\s+\-+\s+extracted$/ ||
/(^\s*)(\S+)\s+\-\ Melted\s*$/) {
$FullName = $2;
# print "DEBUG: >>$FullName<< \t[Rule '$1']\n";
# Count files extracted
++$DisCount;
$err = EvalScan($destdir, $mainfile);
} # extracted file
# Comment
else {
if ($Deferred) { # Print deferred 'Archive' line
$zipcomm .= "$Deferred\n";
$Deferred = "";
}
$zipcomm .= "$_\n"
} # comment
} # while current archive
close $Z;
} # else: archive
return $err;
} # dissolve_file
# -----------------------------------------------------------------------------
sub EvalScan {
local ($destdir, $mainfile) = @_;
local ($destd_strip = $destdir) =~ tr/\//./;
local $err = 0;
# Filenames with extension: path/file.ext
# Fullname must contain full pathname, and NestedName must not.
# \s matches whitespace or newline
if ($FullName =~ /(\S+)\.([0-9A-Za-z\+\-\_\~\^\$\*\%\!\|]+)\s*$/) {
$NestedName = $1;
(($NestedType = $2)) || die "Filename extraction failed";
($destd_strip = $destdir) =~ tr/\//./;
$NestedName =~ s/$destd_strip\///; # Strip 'path/'
# print "DEBUG: $NestedName \tType '$NestedType']\n";
if ($NestedType =~ /^arc|^arj|^lha|^lzh|^zip|^zoo|^rar/i) {
# Do recursion for each sub-archive encountered
# Absolute path to the archive is supplied.
++$IF_Archive;
printf STDERR "\nRecursion for sub-archive $NestedName.$NestedType\n";
++$ZL;
# dest. dir source dir filename
$err = dissolve_file("$destdir/$NestedName", $destdir, "$NestedName.$NestedType");
# if ($err != $FILE_OK) {
# DoError($DS_DIS, $err);
# } # error
--$ZL; # Return to previous input
$Z = "Z_$ZL";
} # is sub-archive
elsif ($EvaluateFiletypes) { # Flag file types included
if ($NestedType =~ /^txt|^doc|^e?ps|^faq|^man|^htm|^swg/i) {
$DocsList .= "$1 "; # Sort these out later...
++$IF_Docs;
parse_txt_type($NestedName, $NestedType);
if (scan_txt($FullName) != $DOC_BINARY && $CreateText) {
create_text($FullName);
}
}
elsif ($NestedType =~ /^diz|^nfo|^log|^lsm|^www|^bbs/i) {
++$IF_Miscinfo;
parse_txt_type($NestedName, $NestedType);
if (scan_txt($FullName) != $DOC_BINARY && $CreateText) {
create_text($FullName);
}
}
elsif ($NestedType =~ /^exe|^com/i) {
++$IF_Exec;
}
elsif ($NestedType =~ /^ini|^sig/i) {
++$IF_Config;
}
elsif ($NestedType =~ /^hlp|^rtf|^pif/i) {
++$IF_Docs;
++$IF_Winapp;
}
elsif ($NestedType =~ /^dll/i) {
++$IF_Winapp;
}
elsif ($NestedType =~ /^cur/i) {
++$IF_System;
++$IF_Winapp;
}
elsif ($NestedType =~ /^fnt|^pal|^sys/i) {
++$IF_System;
}
elsif ($NestedType =~ /^\S\S_/i) {
++$IF_Install;
}
elsif ($NestedType =~ /^dsm|^s3m|^voc|^wav/i) {
++$IF_Data_audio;
++$IF_Data;
}
elsif ($NestedType =~ /^dat|^3ds|^bbm|^bmp|^lbm|^map|^gif|^jpg|^pcx|^pic|^ppm|^raw|^tga/i) {
++$IF_Data_image;
++$IF_Data;
}
elsif ($NestedType =~ /^lib/i) {
++$IF_Object;
# ++$IF_Library;
}
elsif ($NestedType =~ /^a$|^o$|^obj|^bgi/i) {
++$IF_Object;
}
elsif ($NestedName =~ /makefile/i ||
$NestedType =~ /(^asm|^bas|^cc*$|^cpp|^hh*$|^hpp|^hxx|^inc|^mak|^prj|^pas|^tpu|^s$)/i) {
++$IF_Source;
# $1 is source type
$SourceList .= "$1 "; # Sort these out later.
}
# Readme file
elsif ($NestedName =~ /^\!*read$|readme|read1st|descript|howto/i ||
$NestedType =~ /^me|^now|^1st/i) {
++$IF_Readme;
parse_txt_type($NestedName, $NestedType);
if (scan_txt($FullName) != $DOC_BINARY && $CreateText) {
create_text($FullName);
}
}
else {
# Unknown filetypes may be text files or archives.
# Warn the user about binary files encountered.
++$IF_UnknownType;
$Debug &&
print L "ERROR\t$mainfile\tUnknown filetype '$NestedType'.\n";
# $Verbose && print "unknown filetype '$NestedType'\n";
}
} # Evaluate
} # File with Extension
# Filenames without extension: path/file
elsif ($EvaluateFiletypes) { # Flag filetypes included
$NestedName = $FullName;
$NestedName =~ s/$destd_strip\///; # Strip 'path/'
if ($NestedName =~ /makefile/i) {
++$IF_Source;
}
elsif ($NestedName =~ /^\!*read$|readme|read1st|descript|howto/i) {
++$IF_Readme;
parse_txt_type($NestedName, ""); # No extension
if (scan_txt($FullName) != $DOC_BINARY && $CreateText) {
create_text($FullName);
}
}
elsif ($NestedName =~ /^\invoice/i) {
++$IF_Miscinfo;
parse_txt_type($NestedName, ""); # No extension
if (scan_txt($FullName) != $DOC_BINARY && $CreateText) {
create_text($FullName);
}
}
} # Evaluate
return $err;
} # EvalScan
# -----------------------------------------------------------------------------
sub parse_txt_type {
local ($name, $type) = @_;
if ($name =~ /^\!*read$|readme|descript/i) {
}
# Shareware vs. commercial needs to be figured out by
# reading the documentation...
# Try to find mention of Author, Company, or Copying policy
# Note: Licence may be GNU Copyleft
# if ($name =~ /copyri*gh*t*|copyrite|copying|policy/i) {
# ++$IF_Shareware;
# }
# if ($name =~ /copyri*gh*t*|copying/i) {
# ++$IF_Shareware;
# }
if ($name =~ /order|licen[cs]e|invoice/i) {
++$IF_Commercial;
}
if ($name =~ /register/i) {
++$IF_Has_register;
}
} # parse_txt_type
# -----------------------------------------------------------------------------
#
# If there is no text file provided, concatenate document files found inside
# the archive to create one.
#
sub create_text {
local ($txt, $foo) = @_;
local $n;
# chop CR LF and EOF
printf STDERR "DEBUG: CreateText '$txtfile'\n";
if (!$txtfile) {
$n = "$FTP_INCOMING/$mainfile.txt-create";
printf "CreateText: Append '$txt' to '$n'.\n";
### Clean up non-ASCII ###
if ($DosGraphics) { # Remove non-ascii DOS graphics
system ("echo $txt >>$n; tr -d '\001-\010\015\032\200-\377' <$txt >>$n");
}
else { # chop BELL CR LF and EOF
system ("echo $txt >>$n; tr -d '\001-\010\015\032' <$txt >>$n");
}
} # no txt
} # create_text
# -----------------------------------------------------------------------------
#
# Move Checked Files onto target directory
#
# -----------------------------------------------------------------------------
#
# List of checked files and their categories is obtained from STATUSFILE
#
# RECENT file updater, also moves files to right places according to their
# Type fields in *.txt files.
#
sub move_mainloop {
# Read logs
open (rst, "$STATUSFILE") || die "Can't open $STATUSFILE";
open (ML, ">>$TESTLOG") || die "Can't open $TESTLOG"; # MoveLog
printf STDERR "\n--=--=-=--=--=--=--=--=--=--=--=--=--=--=--=--\n";
printf STDERR "\t*** MOVE FILES ***\n";
printf STDERR "--=--=-=--=--=--=--=--=--=--=--=--=--=--=--=--\n";
printf STDERR "Reading Test Status Log\n";
while () {
chop;
if (/^\s*$/) { next; } # Empty line
# $Verbose && print $_; # View processing...
if (/^\s*REMARK/) {
next;
}
elsif (/^\s*CWD\s+(\S+)/) {
$Path = $1;
print $_;
}
# Find tested files on the status list.
elsif (/^\s*CHECK\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)/) {
$M_CdromStatus{$3} = $1;
$M_Category{$3} = $2;
$M_Mainfile{$4} = $3; # Notice the name location ...
$M_Txtfile{$3} = $4;
$M_Path{$3} = $Path;
} # CHECK
elsif (/^\s*CONTENT\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)\s+(\S+)/) {
$M_DisCount{$1} = $2; # Total file count
$M_IF_readme{$1} = $3; # Flag file types included
$M_IF_docs{$1} = $4;
$M_IF_miscinfo{$1} = $5;
$M_IF_source{$1} = $6;
$M_IF_exec{$1} = $7;
$M_IF_config{$1} = $8;
$M_IF_object{$1} = $9;
$M_IF_winapp{$1} = $10;
$M_IF_install{$1} = $11;
$M_IF_data{$1} = $12;
$M_IF_data_audio{$1}= $13;
$M_IF_data_video{$1}= $14;
$M_IF_archive{$1} = $15;
$M_IF_system{$1} = $16;
$M_IF_UnknownType{$1}=$17;
$M_IF_shareware{$1} = $18;
$M_IF_commercial{$1}= $19;
$M_IF_Has_register{$1}= $20;
} # CONTENT
# Uploader (2 sources) and upload mode
elsif (/^\s*MODE\s+(\S+)\s+(\S+)\s/) {
$M_BinMode{$1} = $2;
$M_From{$1} = $3;
$M_UploadDate{$1} = 5;
}
elsif (/^\s*MAIL\s+(\S+)\s+(\S+)\s+(\S+)\s/) { # 1st Mail rule
$M_Uploader{$1} = $2;
$M_Author{$1} = $3;
}
elsif (/^\s*MAIL\s+(\S+)\s+(\S+)\s/) { # 2nd Mail rule
$M_Uploader{$1} = $2;
$M_Author{$1} = "";
}
# Info
elsif (/^\s*SHORT\s+(\S+)\s+(.+)/) { $M_Short{$1} = $2; }
elsif (/^\s*SOURCE\s+(\S+)\s+(.+)/) { $M_Source{$1} = $2; }
elsif (/^\s*KEYWORDS\s+(\S+)\s+(.+)/) { $M_Keywords{$1} = $2; }
elsif (/^\s*REPLACE\s+(\S+)\s+(.+)/) {
$M_ReplaceList{$1} = $2;
++$M_IsUpdate{$1};
}
# Virus Test Log
elsif (/^\s*CLEAN\s+(\S+)/) { $M_VirusTest{$1} = $VS_OK; }
elsif (/^\s*VIRUS\s+(\S+)\s+(.+)/) {
$M_VirusTest{$1} = $VS_INFECTED;
$M_VirusList{$VirusCnt++} = $2;
}
# Misc
# ZipTest Log (Note: 'dosmove' stores these on MOVE/HOLD/JUNK entries.)
# elsif (/^\s*ok\s+(\S+)/) { $M_ArchiveTest{$1} = $AS_OK; }
# elsif (/^\s*ERROR\s+(\S+)/) { $M_ArchiveTest{$1} = $AS_CORRUPT; }
else {
printf STDERR "Ambiguous keyword '$_'.\n";
}
} # while
printf "\n";
# Move
#
# If no filenames are specified on the command line, ask for each file
# listed on the Status log.
#
if (! $ARGV[0]) { @ARGV = sort {$a cmp $b} (keys %M_Txtfile); }
(! $ARGV[0]) && die "Filename(s) to move must be specified.\n";
$MoveCnt = 0;
# Loop for each file
foreach $file (@ARGV) {
# View processing...
printf "\t%s\t%8s\t%s\n", $M_Category{$file}, $file, $M_Txtfile{$file};
$MoveStatus = move_one_file ($M_Path{$file}, $file, $M_Txtfile{$file},
$M_Category{$file}, $M_Uploader{$file},
$M_CdromStatus{$file}, $M_Short{$file}, $M_Keywords{$1});
# Log MOVE, HOLD or JUNK
# Moved ? Only one of these conditions should be true.
if ($MoveStatus == $FILE_MOVE) {
printf ML "MOVE\t%s\t%s\n", $file, $FinalCategory;
++$MoveCnt;
}
elsif ($MoveStatus == $FILE_HOLD) {
printf ML "HOLD\t%s\t%s\n", $file, $error; # re-upload needed
}
elsif ($MoveStatus == $FILE_JUNK) {
printf ML "JUNK\t%s\t%s\n", $file, $error; # rejected for good
next;
}
else {
printf "***ERRCODE\t%s\t%s\n", $file, $MoveStatus;
next;
}
# Finally, after successfull move, remove old files according to
# 'Replaces:' field.
if ($M_ReplaceList{$file}) {
printf "Remove old files: $M_ReplaceList{$file}\n";
# chop ($ans = >> ";
chop ($action = );
printf "\n";
if ($action =~ /^\s*[Bb]/) { # Add file on Blacklist
junk ($src, $mainfile, $txtfile, $REJECTDIR);
add_blacklist ($mainfile, $from);
return $FILE_JUNK;
}
elsif ($action =~ /^\s*[Hh]/) {
junk ($src, $mainfile, $txtfile, $HOLDDIR);
return $FILE_HOLD;
}
elsif ($action =~ /^\s*[Jj]/) {
junk ($src, $mainfile, $txtfile, $REJECTDIR);
return $FILE_JUNK;
}
elsif ($action =~ /^\s*[Mm]/) {
++$done;
}
elsif ($action =~ /^\s*[Nn]/) { # Next file
return $FILE_SKIP;
}
elsif ($action =~ /^\s*[Qq]/) { # Quit program
printf "Really quit ? ";
chop ($ans = );
if ($ans =~ /^\s*[Yy]/) {
exit 2;
}
}
# Misc
elsif ($action =~ /^\s*[Ll]/) { # List files to move.
foreach $file (@ARGV) {
printf "%-8s %-8s\t%s\n",
$M_Category{$file}, $file, $M_Keywords{$file};
# $M_Path{$file}, $file, $M_Txtfile{$file},
# $M_Category{$file}, $M_Uploader{$file},
# $M_CdromStatus{$file}, $M_Short{$file}, $M_Keywords{$1};
} # for
}
# Edit Values
elsif ($action =~ /^\s*[Cc]/) {
$cd = ask_cdrom_status ($cd);
}
elsif ($action =~ /^\s*[Dd]/) {
list_categories();
$categ = ask_category();
}
elsif ($action =~ /^\s*[Kk]/) {
print "Enter keywords";
chop ($keywords = );
}
elsif ($action =~ /^\s*[Ss]/) {
printf "Short:\t%s\n", $short;
print "Enter short description\n> ";
chop ($short = );
}
elsif ($action =~ /^\s*[Tt]/) {
printf "\n$txtfile:\n";
system ("more $src/$txtfile");
}
elsif ($action =~ /^\s*[Uu]/) {
printf ("*** Re-scanning text file '$txtfile'\n\n");
clear_records();
scan_txt ("$src/$txtfile");
printf STDERR "DEBUG: return from Scan on line 3307.\n";
$err = check_records ();
# do write_keys ($mainfile, 1); # Log key fields OK
# Set the local variables accordingly
# $src = ;
$from = $Uploader;
$categ = $RealCategory;
$cd = $CdromStatus;
$short = $Short;
$keywords = $Keywords;
display_values ($mainfile, $categ, $short, $keywords, $cd, $from);
}
elsif ($action =~ /^\s*[Vv]/) {
display_content($mainfile);
display_values ($mainfile, $categ, $short, $keywords, $cd, $from);
}
else { print "Invalid move.\n"; }
} # done
# Move selected -- do Final check
display_values ($mainfile, $categ, $short, $keywords, $cd, $from);
# Check Category is valid.
if (!$categ) {
printf STDERR "No Category for %s\n", $mainfile;
list_categories();
$categ = ask_category();
}
if (($err = &check_prg_category($categ)) == $FILE_OK) {
$categ = $cat;
}
else {
### print "***Invalid Category\n"; ## printed elsewhere
list_categories();
$categ = ask_category();
}
if (!$short) {
printf STDERR "No SHORT-field for %s\n\n", $mainfile;
printf "Enter short description: ";
chop ($short = );
}
if ($AskMv) {
printf "Move file ? ";
chop ($ans = );
if (!($ans =~ /^\s*[Yy]/)) {
return $FILE_SKIP;
}
}
# Check Replace
if ($ReplaceList) {
printf ML "REPLACE\t \n";
} # Replace
# Move files
$FinalCategory = $categ;
$dirri = "$FTPDIR/programming";
$err = do_mv ($src, $mainfile, $txtfile, "$dirri/$categ", $short);
if ($err != $FILE_OK) {
return $err;
}
# Update the CD-ROM database
$err = update_cdrom ("$dirri/$categ", $mainfile, $cd);
if ($err != $FILE_OK) {
printf STDERR "ERROR: Failed to update CD-ROM database.\n";
printf ML "ERROR %d: Failed to update CD-ROM database for %s.\n",
$err, $mainfile;
return $err;
}
return $FILE_MOVE; # Move successfull
} # move_one_file
# -----------------------------------------------------------------------------
sub ask_category {
printf "\nEnter Category\n";
chop ($ans = );
$err = &check_prg_category($ans);
return ($cat);
}
# -----------------------------------------------------------------------------
# List Categories available.
sub list_categories {
printf "\nAvailable Categories on MSDOS Programming\n";
system ("cd $FTPDIR/programming; ls -d [a-z]*");
}
# -----------------------------------------------------------------------------
#
# This routine actually moves the file specified. Upon success, no questions
# are asked.
#
sub do_mv {
local ($src, $mainfile, $txtfile, $dest, $short) = @_;
if (!$src || !$dest || !$mainfile) {
printf STDERR "Error: Filename parameter(s) missing.\n";
return $NO_CATEGORY;
}
# Check source directory is not the target.
if ($dest eq $src) {
return $BAD_CATEGORY;
}
if (! -d $dest || ! -x $dest) {
return $BAD_CATEGORY;
}
$desti = join('/', $dest, $mainfile);
$indexi = join('/', $dest, "00index.txt");
# Check for overwriting
# If the target file exists, is it identical with the new file ?
if (-e $desti) {
# Get the old Short Description.
# `grep $mainfile $indexi`;
# Compare files
if (`diff $src/$file $desti` == 0) {
# 0 No differences were found.
# 1 Differences were found.
# >1 An error occurred.
print "Ignoring duplicate. File already moved: '$mainfile'.\n";
printf ("rm $rmopt -- $mainfile $txtfile\n");
# system ("rm $rmopt -- $src/$mainfile $src/$txtfile");
}
else {
printf "'%s': File exists. Overwrite ? ", $desti;
chop ($ans = );
if (!($ans =~ /^\s*[Yy]/)) {
return $FILE_EXISTS;
}
}
} # exists
# Copy the mainfile onto appropriate category, set permissions, and
# finally remove the original from incoming. Move the txtfile
# to $TXTDIR directory.
printf "*** Moving $mainfile to $dest\n";
printf ML "Moving $mainfile to $dest\n";
system("cp $src/$mainfile $desti");
chmod 0664, $desti;
printf( "rm $rmopt -- $src/$mainfile\n");
# system( "rm $rmopt -- $src/$mainfile");
rename ($txtfile, "$TEXTDIR/$txtfile");
if (! open (TIF, ">>$TEXTDIR/00index.txt")) {
print "Cannot write $TEXTDIR/$txtfile";
print L "ERROR\t$mainfile\tCannot write $TEXTDIR/$txtfile";
}
printf TIF "$dest\t$mainfile\t$txtfile\n";
close TIF; # Index for .txt files.
# Remove work
delete_work ($categ, $mainfile, $txtfile);
# Short: field voi olla monta merkkia pitka.
# printataan stringiin, josta sitten vasta ulos -->
# filename saa olla pitkakin ... (katkaistaan descriptionista)
$krok = sprintf("%4dK %-25s %-.48s", $koko, $dest, $short);
printf (outfile "%.79s\n",$krok); # recent files log
$krak = sprintf("%-8s\t%-.64s", $mainfile, $short);
# Lisataan 00index.txt-fileisiin
system("echo >> $indexi '$krak'");
system("sort < $indexi > $indexi.new");
system ("diff $indexi $indexi.new");
rename ("$indexi.new", $indexi);
chmod 0664, $indexi;
return $FILE_OK;
} # do_mv
# -----------------------------------------------------------------------------
#
# Delete files on the Public Archive
#
# WARNING: May remove the new file as well, if run in the wrong order.
#
sub do_rm {
local ($cat, $rmlist) = @_;
if ($rmlist) {
print "\nFiles: $rmlist\n";
# if ($delete == $VERFY) {
print "Really delete these files? (y/n) ";
chop ($rmverify = );
# }
if ($rmverify eq 'y') {
# Backup
# if ($backup) {
# system ("mv $file $BACKUPDIR");
# ++$saved;
# }
print "Deleting files on FTP archive\n";
# system "rm -- $rmlist";
# Remove old entries in the 00index.txt file
system("grep -w -v '^$file' < $indexi > $indexi.new");
system("diff $indexi $indexi.new");
# rename $indexi.new, $indexi;
chmod 0664, $indexi;
}
else {
print "Files kept.\n";
}
} # rmlist
} # do_rm
# -----------------------------------------------------------------------------
#
# CD-ROM Database Maintenance routines
#
# -----------------------------------------------------------------------------
sub ask_cdrom_status {
local ($old_stat, $foo) = @_;
local $c = $old_stat;
printf "\nCDROM Distrib.:\t%s\n", $CDString[$old_stat];
printf "Enter new status: Yes, No, Perm, Asked\n";
chop ($ans = );
if (($ans =~ /^yes/i) || ($ans =~ /^sure/i)) {
$c = $CD_YES;
}
elsif (($ans =~ /^no/i) || ($ans =~ /^forbid/i)) {
$c = $CD_NO;
}
elsif (($ans =~ /prior|^written|^perm|require[ds]/i)) { # insensitive
$c = $CD_PERM;
}
elsif (($ans =~ /^asked/i)) {
$c = $CD_ASKED;
}
else {
printf STDERR "Unknown or malformed CD-ROM status '$ans'\n";
}
return $c;
} # ask_cdrom_status
# -----------------------------------------------------------------------------
# Update the CD-ROM database
sub update_cdrom {
local ($cat, $mainfile, $cd) = @_;
local $comment = "";
if(! $mainfile || ! -f "$FTPDIR/$cat/$mainfile") {
printf (STDERR "Error: Invalid or missing mainfile '$cat/$mainfile'.\n");
return $NO_MAINFILE;
}
check_cdrom_category ($cat, $mainfile);
if (!$CDPrefix[$cd]) {
printf STDERR "***Invalid CD_ROM mode for '$mainfile'\n";
return $BAD_CDVALUE;
}
if ($dirperm != $CD_NONE && $dirperm != $cd) {
printf "File permission conflicts directory's default.\n";
}
$cd = ask_cdrom_status ($cd);
# Claimed yes, but contents seem suspicious
# if ($IF_commercial && $cd != $CD_NO) {
# $cd = $CD_CONFL;
# printf STDERR "\n*** CD-ROM Status Conflicts.\n"; # CD Conflict
# }
$cdromfile = $cdcat . "/" . $CDPrefix[$cd] . $mainfile;
print "Enter CD-ROM comment. End with a .\n";
while ($c = ) {
last if $c =~ /^\.\s*$/;
$comment .= $c;
}
if ($comment) {
;
}
return $FILE_OK;
} # update_cdrom
# -----------------------------------------------------------------------------
# Delete entry from the CD-ROM database
sub delete_cdrom {
local ($dest, $mainfile, $cd) = @_;
local $err = 0;
# $err = check_cdrom_category();
if (-e $mainfile) {
return $NO_MAINFILE;
}
return $FILE_OK;
} # delete_cdrom
sub check_cdrom_category {
local ($cat, $mainfile) = @_;
$dirperm = $CD_NONE;
if ( -d "$CDROMDIR/$cat") {
$cdcat = "$CDROMDIR/$cat";
}
elsif ( -f "$CDROMDIR/ON_$cat") {
$cdcat = "$CDROMDIR/ON_$cat";
$dirperm = $CD_YES;
}
elsif ( -f "$CDROMDIR/OFF_$cat") {
$cdcat = "$CDROMDIR/OFF_$cat";
$dirperm = $CD_NO;
}
else {
return $BAD_CATEGORY; # Error in the database
}
if (! -d "$CDROMDIR/$cdcat" || ! -x "$CDROMDIR/$cdcat") {
printf STDERR "Error: Cannot access '%s/%s'.\n", $CDROMDIR, $cdcat;
return $BAD_CATEGORY;
}
return $FILE_OK;
} # check_cdrom_category
# -----------------------------------------------------------------------------
#
# Misc Functions
#
# -----------------------------------------------------------------------------
sub update_indexes {
printf "Running $UPDATE_INDEX\n";
system ("$UPDATE_INDEX");
} # update_indexes
# -----------------------------------------------------------------------------
sub display_values {
local ($mainfile, $categ, $short, $keywords, $cd, $from) = @_;
printf "\nFile:\t%s\n", $mainfile;
printf "Category:\t%s\n", $categ;
printf "Short:\t\t%s\n", $short;
printf "Keywords:\t%s\n", $keywords;
printf "CDROM Distrib.:\t%s\n", $CDString[$cd];
# printf "Replaces:\t%s\n", $M_ReplaceList{$mainfile};
printf "Uploader:\t%s\n\n", $from;
if (!$categ) {
printf STDERR "*** No Category for %s\n", $mainfile;
}
if (!$short) {
printf STDERR "*** No SHORT-field for %s\n", $mainfile;
}
} # sub display_values
# -----------------------------------------------------------------------------
# Flag file types included
sub display_content {
local ($file, $foo) = @_;
printf "\n$file\n";
printf " %2d readme files.\n", $M_IF_readme{$file};
printf " %2d document files.\n", $M_IF_docs{$file};
$M_IF_miscinfo{$file} &&
printf " %2d misc info files.\n", $M_IF_miscinfo{$file};
printf " %2d program source files.\t%s\n", $M_IF_source{$file}, $M_SourceList{$file};
printf " %2d executable program files.\n", $M_IF_exec{$file};
$M_IF_config{$file} &&
printf " %2d config data files.\n", $M_IF_config{$file};
$M_IF_object{$file} &&
printf " %2d compiler object files.\n", $M_IF_object{$file};
$M_IF_winapp{$file} &&
printf " %2d Windows application files.\n", $M_IF_winapp{$file};
$M_IF_install{$file} &&
printf " %2d Install datafiles.\n", $M_IF_install{$file};
$M_IF_data{$file} &&
printf " %2d data files.\n", $M_IF_data{$file};
$M_IF_archive{$file} &&
printf " %2d archive files.\n", $M_IF_archive{$file};
$M_IF_system{$file} &&
printf " %2d system files.\n", $M_IF_system{$file};
$M_IF_UnknownType{$file} &&
printf " %2d unknown type files.\n", $M_IF_UnknownType{$file};
printf " %3d files total.\n", $M_DisCount{$file};
printf " %2d shareware.\n", $M_IF_shareware{$file};
printf " %2d commercial.\n", $M_IF_commercial{$file};
# $M_ReplaceList{$file} && printf "\n file is update.\n";
printf "\n";
} # display_content
# -----------------------------------------------------------------------------
# HOLD or JUNK a file
sub junk {
local ($src, $mainfile, $txtfile, $dest) = @_;
local $c = '';
if (!$src || !$dest || !$mainfile) {
printf STDERR "Error: Filename parameter(s) missing.\n";
return $NO_CATEGORY;
}
printf "Do you really want to JUNK '$mainfile' ? ";
chop ($ans = );
if (!($ans =~ /^\s*[Yy]/)) {
return $FILE_SKIP;
}
# Move the file to HOLD or REJECT directory
rename ("$src/$mainfile", "$dest/$mainfile");
rename ("$src/$txtfile", "$dest/$txtfile");
# Add file to Rejectlog
if (!open (R, ">>$REJECTLOG")) {
printf "Cannot open Reject Log file.\n";
return;
}
printf "Enter Rejection comment (only one line):\n> ";
chop ($c = );
printf R "\t%-8s\t- %s\n", $mainfile, $c;
close R;
} # junk
sub add_blacklist {
local ($file, $from) = @_;
local $c = '';
printf "File:\t%s\n", $file;
printf "Uploader:\t%s\n", $from;
printf "Enter Blacklist comment (only one line):\n> ";
chop ($c = );
# Write entry to Blacklist
printf "Are you sure ? ";
chop ($ans = );
if (!($ans =~ /^\s*[Yy]/)) {
return $FILE_SKIP;
}
open (B, ">>$BLACKLIST") || die "Cannot write Blacklist file.\n";
printf B "%s\t%s\t%s\n", $file, $from, $c;
close B;
} # add_blacklist
# -----------------------------------------------------------------------------
# Recursively delete the dissolved file from the PCWORK directory
sub delete_work {
local ($cat, $mainfile, $txtfile) = @_;
if ( -e "$PCWORK/$categ/$mainfile") {
printf STDERR "Remove '%s' on workdir '%s/%s'.\n",
$mainfile, $PCWORK, $cat;
system( "/bin/rm -rf $PCWORK/$cat/$mainfile*");
system( "/bin/rm -f $PCWORK/$cat/$txtfile");
}
return $FILE_OK;
} # delete_cdrom