#!perl -w ############################################################ ## editLargefile ## Written By: Rohit Mishra ( rohit[at]rohitmishra.com) ## Modified for Win32 by Bob Friedman (friedmar[at]biol.sc.edu) ############################################################ ## Keywords: BEGIN, INC, tempfile, FileHandle, stat, tempdir, ## select, TextUndo, bind, configure, sysread, ## syswrite, update, numberChanges, messageBox ############################################################ ### TO SAVE A FILE, CHOOSE MENU ITEM "SAVE THIS BUFFER" BEFORE CHOOSING TO SAVE FILE ### Use following modules use strict; use Tk; use File::stat; use FileHandle; use File::Path; use File::Spec; use File::Temp qw/ tempfile tempdir /; require Tk::TextUndo; require Tk::FileSelect; require Tk::Table; ### Global Defines ### my $NO = 0; my $YES = 1; my $CANCEL = -1; my $ver = '1.0w'; my $bg_color = "white"; my $dir = "./"; my $template = ".largefile.XXXX"; my @tmpfile_array = (); my $current_file; my $no_page = $YES; my $open_status = 0; my $FILENAME = undef; ### To save the split file ### my $split_dir = "."; my $split_file = "file"; my $extn = 0; ### Temporary Directory functions ### my $tempdir = tempdir ( $template, DIR => $dir , CLEANUP => 1 ); ### Number of pages ### my $HOME = "."; my $number_of_pages = 10; my $tmpfile_size; my $xsize=600; my $ysize=900; my $font="-adobe-courier-medium-r-normal--14-100-100-100-m-90-iso8859-1"; if( ! -e "$HOME/.largefile.rc" ){ open( RC,">$HOME/.largefile.rc" ) || die("$!"); print RC "VERSION=1.5win\n"; print RC "PAGES=10\n"; print RC "XSIZE=600\n"; print RC "YSIZE=900\n"; print RC "FONT=-adobe-courier-medium-r-normal--14-100-100-100-m-90-iso8859-1\n"; print RC "BACKGROUND=white\n"; print RC "FOREGROUND=black\n"; ### TOOLBAR CODE REMOVED FROM ALL SOURCE CODE # print RC "TOOLBARDIR=./toolbar\n"; close(RC); } else { open( RC,"$HOME/.largefile.rc" ) || die("$!"); my @setup = ; close(RC); foreach my $line ( @setup ){ if( $line =~ /VERSION\s*=\s*(\S+)/ ){ $ver = $1; } if( $line =~ /PAGES\s*=\s*(\S+)/ ){ $number_of_pages = $1; } if( $line =~ /XSIZE\s*=\s*(\S+)/ ){ $xsize = $1; } if( $line =~ /YSIZE\s*=\s*(\S+)/ ){ $ysize = $1; } if( $line =~ /BACKGROUND\s*=\s*(\S+)/ ){ $bg_color = $1; } if( $line =~ /FONT\s*=\s*(\S+)/ ){ $font = $1; } # if( $line =~ /TOOLBARDIR\s*=\s*(\S+)/ ){ # $toolbardir = $1; # } } } ### Main Code Begins ### my $mw = MainWindow->new( -title => "LargeFile Editor $ver" ); $mw->minsize( $ysize,$xsize ); my $menubar1 = $mw->Frame(-relief => 'raised', -borderwidth => 2); my $menubar2 = $mw->Frame(-relief => 'raised', -borderwidth => 2); ## File Menu ## $menubar1->Menubutton(qw/-text File -underline 0 -tearoff 0 -menuitems/ => [ [Button => 'Open ..', -command => sub{ if( $open_status == 0 ){ $open_status = 1; $FILENAME = &Open_LargeFile; $open_status = 0; } }], [Button => 'Save this buffer ..', -command => sub{ &On_Save( $FILENAME ); }], [Button => 'Save Largefile ..', -command => sub{ &Save_LargeFile( $FILENAME ); }], [Button => 'Save Largefile As ..', -command => sub{ if( defined $FILENAME ){ if( $open_status == 0 ){ $open_status = 1; &On_SaveAs; $open_status = 0; } } else { my $dialog = $mw->Dialog(-title => 'Warning', -text => 'No File opened!!'); $dialog->Show('-global'); } }], [Separator => ''], [Button => 'Close ..', -command => sub{ &On_Close; }], [Separator => ''], [Button => 'Quit', -command => sub{ &On_Exit( $FILENAME ); }], ])->pack(-side=>'left'); ## Edit Menu ## $menubar1->Menubutton(qw/-text Edit -underline 0 -tearoff 0 -menuitems/ => [ [Button => 'Undo', -command => sub{ &On_Undo; }], [Button => 'Redo', -command => sub{ &On_Redo; }], [Separator => ''], [Button => 'Select All', -command => sub{ &On_SelectAll; }], [Button => 'Unselect All', -command => sub{ &On_UnselectAll; }], ])->pack(-side=>'left'); ## View Menu ## $menubar1->Menubutton(qw/-text View -underline 0 -tearoff 0 -menuitems/ => [ [Button => 'Previous Page', -command => sub{ &Traverse_To_Prev; }], [Button => 'Next Page', -command => sub{ &Traverse_To_Next }], [Separator => ''], [Button => 'Go To Page', -command => sub{ &Open_Page; }], ])->pack(-side=>'left'); my $InputText = $mw->Scrolled('TextUndo', -height => '1', -width => '1', -scrollbars => 'osoe', -background=>$bg_color,-font => $font,) ; my $Basemenu = $mw->Frame(-relief => 'raised', -borderwidth => 2); my $Lbl_Page_No = $Basemenu->Label( -text => 'No Page Open' ); ### Pack everything ### $menubar1->form( -top=> '%0', -left => '%0', -right => '%100' ); $menubar2->form( -top=> $menubar1, -left => '%0', -right => '%100' ); $InputText->form( -top=> $menubar2, -left => '%0', -right => '%100', -bottom => $Basemenu ); $Basemenu->form( -left=> '%0', -right => '%100', -bottom => '%100' ); $Lbl_Page_No->pack( -side => 'right' ); &MainLoop; ### User Defined Subroutines ### ### File menu callbacks sub Open_LargeFile(){ ## MODIFIED OPEN FILE DIALOG BOX ROUTINE # my $filename = &Open_File( $mw ); my $main = shift; my $filename = $mw->getOpenFile( -title=>'Select File' ); my $sb; my $size; if( ( !defined $filename ) && ( ! -f $filename ) ){ return; } else { $sb = stat( $filename ); $size = $sb->size; my $line; my $i = 0; my $var = 33; my $pages; my $time_left; my $total_time; my $dis_time_left; my $time_for_one_page = 1.0; # seconds my $title = "Opening File: $filename"; # ADDED CODE TO ERROR CHECK FOR SMALL FILES # if <200Kb file size then force on 1 page if($size < 200000){ $number_of_pages = 1; } if( $number_of_pages != 0 ){ $tmpfile_size = $size/$number_of_pages; } else { print "Number of pages cannot be less than 1\n"; exit(0); } # REMOVED THIS IF CONDITION ### if ( $size <= $tmpfile_size ){ # $InputText->Load( $filename ); # $no_page = $YES; # $current_file = 0; # $Lbl_Page_No->configure( -text => "Page No: $current_file" ); # $mw->configure( -title => "LargeFile Editor $ver: $filename" ); ### } else { $pages = $size/$tmpfile_size; $total_time = sprintf("%.2f",( $pages * $time_for_one_page ) ); my $top = $mw->Toplevel( -title => "Please Wait...",-popover => $mw); $top->resizable('no','no'); $top->Label(-textvariable => \$title )->pack; $top->Label(-textvariable => \$dis_time_left )->pack; my $m = $top->Frame(-height => 10, -border => 2, -relief => 'sunken')->pack(-fill => 'x'); my $s = $m->Frame(-background => 'blue', -relief => 'raised', -border => 2); &Busy; open( FILE, "< $filename" ) || die( "Could not open file $filename" ); $var = sysread( FILE, $line, $tmpfile_size ); while( $var != $NO ){ ( undef, $tmpfile_array[$i] ) = tempfile( $template, DIR => $tempdir, OPEN => 0 ); open( TMP,">$tmpfile_array[$i]" ) || die( "Could not open file $tmpfile_array[$i]" ); if( $ARGV[0] =~ m|^\-s| ){ open( SPLIT,">$split_dir/$split_file$extn" ) || die( "Could not open file $split_dir/$split_file$extn" ); } syswrite( TMP, $line ); if( $ARGV[0] =~ m|^\-s| ){ syswrite( SPLIT, $line ); close( SPLIT ); $extn++; } close( TMP ); $i++; $time_left = sprintf("%.2f",( $total_time - ( $i * $time_for_one_page )) ); $dis_time_left = sprintf("Time Left: %.0f mins %.0f seconds",$time_left/60,$time_left%60 ); $s->place('-x' => 0, '-y' => 0, -relheight => 1, -relwidth => ($total_time - $time_left)/$total_time ); $top->update; $var = sysread( FILE, $line, $tmpfile_size ); } $top->destroy; close( FILE ); $current_file = 0; $InputText->Load( $tmpfile_array[0] ); $mw->configure( -title => "LargeFile Editor $ver: $filename" ); $Lbl_Page_No->configure( -text => "Page No: $current_file" ); &Unbusy; $no_page = $NO; ### } return ( $filename ); } } sub Traverse_To_Next(){ my $var = scalar ( @tmpfile_array ) - 1; if( $current_file < $var ){ &main::Tk::TextUndo::ConfirmDiscard( $InputText ); $current_file++; $InputText->Load( $tmpfile_array[$current_file] ); $Lbl_Page_No->configure( -text => "Page No: $current_file" ); } else { $current_file = $current_file; } } sub Traverse_To_Prev(){ if( $current_file != 0 ){ &main::Tk::TextUndo::ConfirmDiscard( $InputText ); $current_file--; $InputText->Load( $tmpfile_array[$current_file] ); $Lbl_Page_No->configure( -text => "Page No: $current_file" ); } else { $current_file = $current_file; } } sub Open_Button_Page(){ my $top = $mw->Toplevel( -title => "Select Page" ); $top->minsize( qw/300 100/); $top->maxsize( qw/300 300/); my $page_container = $top->Table( -rows => '20', -columns => '20',-scrollbars => 'se' ); my $manubar = $top->Frame( -relief => 'sunken' ); my $cancel = $manubar->Button( -text => "Cancel", -command => sub{ $top->destroy; }); $page_container->form( -left => '%0', -top => '%0', -right => '%100', -bottom => $manubar ); $manubar->form( -left => '%0', -right => '%100', -bottom => '%100', ); $cancel->pack( -side => 'left', -fill => 'x', -expand => '1' ); my $no_of_pages = scalar( @tmpfile_array ) - 1 ; my @buttons = (); for( my $i = 0; $i <= $no_of_pages; $i++ ){ $buttons[$i] = $page_container->Button( -text => "$i" ); $page_container->put( $i%10, $i/10, $buttons[$i] ); } return $top, @buttons; } sub Open_Page (){ my $page_no; my ($w, @buttons) = &Open_Button_Page(); foreach my $button ( @buttons ){ $button->bind( '' => sub{ &main::Tk::TextUndo::ConfirmDiscard( $InputText ); $page_no = $button->cget(-text); $Lbl_Page_No->configure( -text => "Page no: $page_no" ); $w->destroy; $InputText->Load( $tmpfile_array[$page_no] ); $current_file = $page_no; }); } } sub On_SaveAs(){ ### CHANGED METHOD FOR SAVING FILE DIALOG BOX my $name = $InputText->getSaveFile(-title => 'File Save As'); # my $name = $InputText->CreateFileSelect( 'getSaveFile',-title => 'File Save As'); return &Save_LargeFile( $name ) if defined($name) and length($name); return 0; } sub On_Undo(){ $InputText->undo; } sub On_Redo(){ $InputText->redo; } sub On_SelectAll(){ $InputText->tagAdd('sel','1.0','end'); } sub On_UnselectAll(){ $InputText->tagRemove('sel','1.0','end'); } sub On_Save(){ my $filename = shift; if( defined $filename ){ $InputText->Save; } else { my $dialog = $mw->Dialog(-title => 'Warning', -text => 'No File opened!!'); $dialog->Show('-global'); } } sub Save_LargeFile(){ my $filename = shift; my $line; if( defined $filename ){ open( FILE,">$filename" ) || $mw->Dialog(-title => 'Warning', -text => 'File not writable.')->Show('-global'); foreach my $tmpfile ( @tmpfile_array ){ open( TMP,"$tmpfile" ) || die( "Could not open swap file $tmpfile\n" ); if( sysread( TMP, $line, $tmpfile_size ) != $NO ){ chomp( $line ); syswrite( FILE, $line ); } close( TMP ); } close( FILE ); } else { my $dialog = $mw->Dialog(-title => 'Warning', -text => 'No File opened!!'); $dialog->Show('-global'); } } sub On_Close(){ if( $InputText->ConfirmDiscard ){ $FILENAME = undef; $InputText->EmptyDocument; @tmpfile_array = (); $mw->configure( -title => "LargeFile Editor $ver" ); $Lbl_Page_No->configure( -text => "No Page Open" ); $no_page = $YES; } else { return; } } sub On_Exit (){ my $filename = shift; if( defined $filename ){ if( -w $filename ){ ### Check Sticky bit here if ( $InputText->numberChanges ){ my $ans = $mw->messageBox(-icon => 'warning', -type => 'YesNoCancel', -default => 'Yes', -message => "The text has been modified without being saved. Save edits?"); if( $ans eq 'Cancel' or $ans eq 'No' ){ exit(0); } elsif( $ans eq 'Yes' ){ $InputText->Save( $tmpfile_array[$current_file] ); &Save_LargeFile( $filename ); exit(0); } } else { exit(0); } } else { if ( $InputText->numberChanges ){ my $ans = $mw->messageBox(-icon => 'warning', -type => 'YesNoCancel', -default => 'Yes', -message => "File not writable. Quit?"); if( $ans eq 'Cancel' or $ans eq 'No' ){ return; } elsif( $ans eq 'Yes' ){ exit(0); } } else { exit(0); } } } else { exit(0); } } sub Busy (){ $mw->Busy; $InputText->Busy; } sub Unbusy (){ $mw->Unbusy; $InputText->Unbusy; } __END__ =head1 NAME editLargefile.pl - Edit files of size greater than 2.8 GB =head1 SYNOPSIS editLargefile.pl =head1 STATUS editLargefile.pl is in its beta version. =head1 DESCRIPTION B is a window based editor with all the editing capabilities a TextUndo text widget has. This editor is recomended for editing a file of size more than 2.8 GB. =head1 FEATURES B uses sysread and syswrite system calls which makes it very efficient. At startup, it creates a .largefile.rc file which contains information about the default settings which a user wishes to use. The opened file is split into smaller chunks of data, which are displayed on a TextUndo widget. When the editing is complete, the user only has to save the chunk which has been modified and not the whole file, which drastically reduces the file saving time. The user can browse through the pages(chunks) serially or can jump directly to the page to be edited. Additionally, all the editing can be un-done or re-done at any point. =head1 BUGS When the editor crashes, it leaves temporary directories with the split files in them. =head1 PREREQUISITES Tk File::stat FileHandle File::Path File::Spec File::Temp Tk::TextUndo Tk::Table Tk::FileSelect =head1 OSNAMES Win32 =head1 AUTHOR Rohit Mishra EFE modified for Win32 by Bob Friedman (friedmar[at]biol.sc.edu) =head1 COPYRIGHT Copyright (c) 2005 Rohit Mishra & [Bob Friedman for Win32 version only]. All rights reserved This program is a free software; you can redistribute it and/or modify it under the same terms as Perl itself. =head1 SCRIPT CATEGORIES Win32 Educational =cut