#!/usr/bin/perl use strict; use warnings; sub _search ($;$); # pre-declare the _search() subroutine sub _delete ($;$); # pre-declare the _delete() subroutine $|++; # autoflush on my $file = shift; # get file from command line unless ( $file and -f $file ) # check if we got a readable file { print "Specify filename to load URLs from: "; # ask the user for a filename to load the URLs from chomp( $file = ); print "\n"; } # unless open( my $in, '<', $file ) # try to open the specified filename or die "open R '$file' failed: $!"; # die on error chomp( my @url_list = <$in> ); # get the URLs, one URL per line close( $in ); my @new_urls = (); # used to save unsaved URLs my @del_urls = (); # used to save deleted URLs my @search = (); # used to save last search/get command return values my $is_saved = 1; # used to mark whether the URL list is saved or not my $done = 0; # used to store if we are done do { print "> "; # ask the user for a command chomp( my $cmd = ); $cmd = lc $cmd; # make the command lowercase $cmd =~ s/\s//; # delete whitespace from the command do{$done=1;goto LOOP} if $cmd eq 'q' or $cmd eq 'quit' or $cmd eq 'exit' or $cmd eq 'bye'; # leave the loop if the user typed q or quit if ( $cmd eq 'a' or $cmd eq 'add' ) # add a new URL? { print "New URL: "; # ask the user for a new URL to add chomp( my $new_url = ); # get the URL if ( grep { $_ eq $new_url } @url_list ) # check if we already have this URL in the list { print "\nURL already in list.\nYou may want to '> s[earch]' or '> see' the URL list.\n\n"; goto LOOP; } # if push( @url_list, $new_url ); # add it to the URL list push( @new_urls, $new_url ); # add it to the 'unsaved' URL list $is_saved = 0; # mark the list as unsaved print "\nURL added.\nYou may want to '> save' the URL list.\n\n"; } # if elsif ( $cmd eq 'd' or $cmd eq 'del' or $cmd eq 'delete' ) # delete some URLs? { print "URL or # "; # ask the user for a URL or a number chomp( my $del_url = ); goto LOOP unless $del_url; my $url_pl = 0; # used to store whether multiple URLs got deleted or not my $del = 0; # used to store if we deleted any URL if ( @search && $del_url =~ /^\d+/ ) # if a search list exists and the user specified a number { $del = _delete( $search[ $del_url-1 ], 0 ) } # delete the URL, do not ask the user before doing it else { _search( $del_url, sub{} ); # search the URL list if ( @search > 1 ) # we have multiple search items? { print "\nWhich URLs do you want to delete? (range) "; # ask the user for the URLs to delete chomp( $del_url = ); $del_url =~ s/\s//g; # delete all spaces from the specified range for my $p ( split( ',', $del_url ) ) # iterate over each part of the range { my @p = split( '..', $p ); # try to split this part $p[1] = $p[0] if @p == 1; # set p.1 so that we can build a range if we have just one value $del = _delete( $search[ $_-1 ], 1 ) for $p[0] .. $p[1]; # delete the URLs, ask the user before doing it } # for $url_pl = 1; # set the URL plural flag to 1 } # if else { $del = _delete( $search[0], 1 ) } # delete the URL, ask the user before doing it } # else if ( $del ) { print "\nURL". ($url_pl ? 's' : '') ." deleted.\nYou may want to '> save' the URL list.\n\n" } else { print "\nProblem while deleting ". ($url_pl ? 'one or more URLs' : 'the URL') .".\nYou may want to '> s(earch)' or '> see' the URL list to get more information.\nYou may also want to '> save' the URL list.\n\n" } } # elsif elsif ( $cmd eq 'c' or $cmd eq 'changes' ) # show changes? { if ( $is_saved ) { print "There are no unsaved changes.\n\n"; goto LOOP; } # if if ( @new_urls ) { print "New URLs till last save:\n"; for my $i ( 1 .. @new_urls ) { if $i % 24 == 0; # stop the output every 24 lines printf "%3s : %s\n", $i, $new_urls[$i-1]; } # for print "\n"; } # if if ( @del_urls ) { print "Deleted URLs till last save:\n"; for my $i ( 1 .. @del_urls ) { if $i % 24 == 0; # stop the output every 24 lines printf "%3s : %s\n", $i, $del_urls[$i-1]; } # for print "\n"; } # if } # elsif elsif ( $cmd eq 's' or $cmd eq 'search' ) # search the URL list? { print "Enter search string: "; # ask the user for the search string chomp( my $search = ); _search( $search, sub{} ); # search the URL list print "\nYou may want to '> d[elete]' or '> a[dd]' some URLs.\n\n"; } # elsif elsif ( $cmd eq 'see' ) # show the full URL list? { _search( '' ); # search for nothing, so get all if ( $is_saved ) { print "\nThese were all URLs in the list.\n\n" } else { print "\nThese were all URLs in the list.\nYou may want to '> save' the URL list, because it has got unsaved changes.\nTo see these changes, please use the '> c[hanges]' command.\n\n" } } # elsif elsif ( $cmd eq 'w' or $cmd eq 'write' or $cmd eq 'save' ) # save the URL list? { print "Type filename to save the list to [$file] "; # ask the user for a filename to save the list to chomp( my $new_file = ); ($file = $new_file) if $new_file; open( my $out, '>', $file ) # try to open the specified filename or die "open W '$file' failed: $!"; # die on error print $out join( "\n", @url_list ); # write the url list to this file clsoe( $out ); @new_urls = (); # reset the 'unsaved' URL list @del_urls = (); # reset the 'deleted' URL list $is_saved = 1; # reset the is_saved flag print "\nURL list saved.\n\n"; } # elsif else { print "Unknown command '$cmd'.\nPossible commands are: a[dd], c[hanges], d[el[ete]], s[earch], see, save/w[rite], q[uit]/exit/bye\n\n"; } # else LOOP: } until $done; unless ( $is_saved ) { if ( @new_urls ) { print "New URLs till last save:\n"; for my $i ( 1 .. @new_urls ) { if $i % 24 == 0; # stop the output every 24 lines printf "%3s : %s\n", $i, $new_urls[$i-1]; } # for print "\n"; } # if if ( @del_urls ) { print "Deleted URLs till last save:\n"; for my $i ( 1 .. @del_urls ) { if $i % 24 == 0; # stop the output every 24 lines printf "%3s : %s\n", $i, $del_urls[$i-1]; } # for print "\n"; } print "Do you really want to lose these changes? (yes/no) [no] "; chomp( my $answer = ); $answer = lc $answer; # make the answer lowercase $answer =~ s/\s//; # delete whitespace from the answer exit 0 if $answer eq 'y' or $answer eq 'yes'; # exit the application, the user does not want to save the changes print "Type filename to save the list to [$file] "; # ask the user for a filename to save the list to chomp( my $new_file = ); ($file = $new_file) if $new_file; open( my $out, '>', $file ) # try to open the specified filename or die "open W '$file' failed: $!"; # die on error print $out join( "\n", @url_list ); # write the url list to this file clsoe( $out ); print "\nURL list saved.\n\n"; } # if # # _search( SEARCH [, WAIT_TIME] ) # This subroutine searches the @url_list array for SEARCH and saves the matches # in @search. # It will wait WAIT_TIME seconds after showing the number of results to the user. # If WAIT_TIME is a code reference, it will run that code reference instead. # The @search array will get printed to the screen with a stop every 24 lines. # sub _search ($;$) { my( $search, $wait ) = @_; @search = grep { /\Q$search\E/ } @url_list; # search the URL list print "\n@{[ 0+@search ]} entries matching '$search'.\n"; if ( $wait && ref( $wait ) eq 'CODE' ) { $wait->() } # run the wait code else { select( undef, undef, undef, $wait || 0 ) } # wait $wait or zero seconds for my $i ( 1 .. @search ) { if $i % 24 == 0; # stop the output every 24 lines printf "%3s : %s\n", $i, $search[$i-1]; } # for } # _search # # _delete( URL [, ASK_BEFORE] ) # This subroutine deletes one URL from the URL list. # It will ask the user before deleting the item if ASK_BEFORE is set to true. # This subroutine will also delete the URL from the 'unsaved' URL list and it will # add all deleted URLs to the 'deleted' URL list and change the is saved flag # respectively. # sub _delete ($;$) { my( $url, $q ) = @_; if ( $q ) # shall we asked the user? { print "$url\nDo you really want to delete this URL? (yes/no) [yes] "; # ask the user if he/she wants to delete the URL chomp( my $answer = ); $answer = lc $answer; # make the answer lowercase $answer =~ s/\s//; # delete whitespace from the answer return 0 if $answer eq 'n' or $answer eq 'no'; # return false if the answer was 'no' } # if my $idx; ($idx) = grep { $url_list[$_] eq $url } 0.. $#url_list; # try to find the URL in the URL list splice( @url_list, $idx, 1 ) if defined $idx; # delete the URL from the URL list return 0 if ! defined $idx; # return false if the URL could not be found push( @del_urls, $url ); # add the deleted URL to the 'deleted' URL list $is_saved = 0; # reset the is_saved flag ($idx) = grep { $new_urls[$_] eq $url } 0.. $#new_urls; # try to find the URL in the 'unsaved' URL list splice( @new_urls, $idx, 1 ) if defined $idx; # delete the URL from the 'unsaved' URL list return 1; } # _delete