/*
// This file is a part of the TMI distribution mudlib.
// Please retain this header if you use this code.
// Devised & Written by Douglas Reay (Pallando @ TMI, Ephemeral Dale, etc)
// Moved to TMI-II (25-11-92)
*/
#include <disclaimer.h>
#include <mudlib.h>
inherit DAEMON;
#define LIMIT 20
#define TAB " "
int waste_ch( string a );
void compress_lines( mixed a );
void resave_file( mixed new_lines, string file_name, object player );
string help();
int cmd_compress( string a ) /* a is the name of the file to be compressed */
{
string old_file;
string *old_lines;
int number_lines;
mixed arg;
string old_file_name, new_file_name;
if( !a ) { help(); notify_fail( "No argument passed.\n" ); return 0; }
if( 2 != sscanf( a, "%s to %s", old_file_name, new_file_name ) &&
2 != sscanf( a, "%s %s", old_file_name, new_file_name ) )
{
old_file_name = a;
new_file_name = a + ".comp";
}
seteuid( getuid( previous_object() ) );
old_file_name = resolv_path( this_player()-> query( "cwd" ), old_file_name );
new_file_name = resolv_path( this_player()-> query( "cwd" ), new_file_name );
switch( file_size( old_file_name ) )
{
case -2: notify_fail( "compress: " + old_file_name + ": directory\n" ); return 0;
case -1: notify_fail( "compress: " + old_file_name + ": no such file\n" ); return 0;
}
if( !master()-> valid_read( old_file_name,
(string)this_player()-> query( "name" ), "compress" ) )
{
notify_fail( old_file_name + ": Permission denied.\n" );
return 0;
}
old_file = read_file( old_file_name );
if( !old_file ) { notify_fail( old_file_name + " is empty.\n" ); return 0; }
if( !master()-> valid_write( new_file_name,
(string)this_player()-> query( "name" ), "compress" ) )
{
notify_fail( new_file_name + ": Permission denied.\n" );
return 0;
}
if( file_size( new_file_name ) != -1 )
{
notify_fail( "compress: " + new_file_name + ": already exists.\n" );
return 0;
}
old_lines = explode( old_file, "\n" );
number_lines = sizeof( old_lines );
arg = ({
0, /* line number to start on */
number_lines, /* the number of lines in the file */
old_lines, /* the file, as an array of its lines */
({ }), /* an array for the new version to go into */
new_file_name, /* the file name for the compressed version */
this_player(), /* so messages can be given by the call_out */
0, /* so text inside comment marks is unaltered */
});
call_out( "compress_lines", 1, arg );
write( "Please wait " + (number_lines/LIMIT + 1) + " seconds.\n" );
return 1;
/*
There is a limit on the number of lines the machine can compress a second.
If the file has more than this number, call_out must be used to
split the processing into chunks to avoid getting the
"too many executions" error.
*/
}
void compress_lines( mixed a ) /* array a contains a mixture of variables */
{
string file_name;
string *old_lines, *new_lines;
string old_line, new_line;
int number_lines, line_number, char_number;
int inside_comment, inside_quotes, line_started, ignore_next;
int line_ended; // used for this type of comment.
int limit, add_ch;
string ch, last_ch, next_ch;
object player;
line_number = a[0];
number_lines = a[1];
old_lines = a[2];
new_lines = a[3];
file_name = a[4];
player = a[5];
inside_comment = a[6];
limit = line_number + LIMIT;
if( limit > number_lines ) limit = number_lines;
for( ; line_number < limit ; line_number ++ )
{
/*************** START OF BIT THAT COMPRESSES A SINGLE LINE ****************/
old_line = old_lines[line_number];
new_line = "";
inside_quotes = 0; /* 0 when outside "" marks, 1 when inside */
line_started = 0; /* 0 until the first non-space char in the line */
line_ended = 0; // 0 until reach this type of comment.
ignore_next = 0; /* Used to cope with \ marks */
next_ch = old_line[0..0];
ch = "";
for( char_number = 0 ; char_number < strlen( old_line ) ; char_number ++ )
{
last_ch = ch;
ch = next_ch;
next_ch = old_line[(char_number+1)..(char_number+1)];
/* NB old_line[(char_number+1)] would get a number not a string */
add_ch = 1; /* ch gets added to new_line if add_ch remains 1 */
if( inside_comment || line_ended ) {
if( ch == "*" && next_ch == "/" ) inside_comment = 0;
/* If you are inside a comment, check for the end of it. */
/* add_ch unaltered - text inside coments is copied verbatim */
} else if( ch != " " && ch != TAB ) {
/* space and tab are the only chars we delete during compression */
line_started = 1; /* we have found a non-space character */
if( ignore_next ) ignore_next = 0;
else if( ch == "\\" ) ignore_next = 1;
else if( ch == "\"" ) inside_quotes = 1 - inside_quotes;
else if( inside_quotes );
else if( ch == "/" && next_ch == "*" ) inside_comment = 1;
else if( ch == "/" && next_ch == "/" ) line_ended = 1;
} else {
if( !line_started ) add_ch = 0;
/* delete all spaces at start of lines */
else if( !ignore_next && !inside_quotes &&
/* don't delete spaces inside quotes or comments */
( next_ch == " " || next_ch == "" ||
/* delete more than single spaces and those at end of lines */
waste_ch( last_ch ) || waste_ch( next_ch ) ||
/* Delete spaces beside certain characters (see waste_ch() ) */
next_ch == TAB || last_ch == "<" ) )
/* Don't delete spaces before < marks because of includes. */
add_ch = 0; /* don't add this ch becuase it is an unwanted space */
}
if( add_ch ) new_line += ch;
}
new_lines += ({ new_line });
/*************** END OF BIT THAT COMPRESSES A SINGLE LINE ****************/
}
/* If havn't finished, compress the next bunch of lines */
if( line_number < number_lines )
{
call_out( "compress_lines", 1, ({
line_number,
number_lines,
old_lines,
new_lines,
file_name,
player,
inside_comment,
}) );
tell_object( player, "Lines " + (line_number-LIMIT) + " to " +
line_number + " compressed.\n" );
return;
}
return resave_file( new_lines, file_name, player );
/* Save the compressed file */
}
/* string array new_lines, string file_name, object player */
void resave_file( mixed new_lines, string file_name, object player )
{
string new_file;
mixed err;
new_file = implode( new_lines, "\n" );
/* turn the array of lines back into one string */
new_file += "\n";
if( err = catch( write_file( file_name, new_file ) ) )
tell_object( player, "Error: " + err + "\n" );
else tell_object( player, "Compressed version saved to: " + file_name + "\n");
}
/*
a is a single character in the form of a string
This function checks if the ch is one of the characters, next
to which spaces can be safely deleted.
*/
int waste_ch( string a )
{
switch( a )
{
case "+": return 1;
case "-": return 1;
case "=": return 1;
case ",": return 1;
case ";": return 1;
case "(": return 1;
case ")": return 1;
case "{": return 1;
case "}": return 1;
case ">": return 1;
case "|": return 1;
case "&": return 1;
case "!": return 1;
default: return 0;
}
/* returns 1 if the space can be deleted, 0 if it can't */
}
string help()
{
write( ""+
"Syntax: compress <file>[ to <new_file>]\n"+
"Effect: Removes spaces in the file that are unnecessary\n"+
" for the code to work and copies the version to\n"+
" <new_file> (which defaults to <file>.comp)\n"+
"" );
}