aboutsummaryrefslogtreecommitdiff
path: root/xcleanpaste
blob: c122002cee899119259eeff2ac2e5f89e75d3c27 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
#!/usr/bin/perl -w

=pod

=head1 NAME

xcleanpaste - join multi-line copy/paste into one long line

=head1 SYNOPSIS

B<xcleanpaste> [-n] [-u] [-e<line-ending>] [-v] [xsel-options]

=head1 DESCRIPTION

"Cleans" the X selection buffer: trims whitespace from the start
and end of every line, replaces all newlines with spaces, replaces
all tabs with 3 spaces each. Result is written back to the selection
buffer, where you can paste it into whatever application you want.

This is useful even for single-line pastes: it removes tabs and any
newline at the end, so e.g. copying a command from a browser into the
shell won't trigger tab-completion or execute the command immediately.

The selection buffer is read/written using xsel, which must be
installed and available on your PATH.

xcleanpaste is best used by binding to a key using xbindkeys or
similar, though you can run it from the shell too.

If you're using keybinding, you probably want at least separate keys
for xcleanpaste with no options and xcleanpaste with the -u option
(URL cleaner; see below).

=head1 OPTIONS

=over 4

=item B<-e><line-ending>

Replace line endings with the given string rather than a space. Don't
use a space between the -e and the ending. Beware of shell quoting
rules.

=item B<-n>

Replace line endings with the string " \n " rather than a space. This is
space, backslash, lowercase n, space. Equivalent to B<-e ' \n '>.

=item B<-u>

Remove CGI arguments from any URLs found in the buffer. Some sites
including tracking info, e.g. https://site.blah/stuff?sessionID=12345,
which you shouldn't be pasting into anything others can see (e.g. IRC
or emails).

=item B<-v>

Verbose mode. Shows lines read and written from the xsel instances on
stderr. Not much use with xbindkeys though.

=back

Any other arguments are passed to xsel. This is probably only useful
for xsel's -p/-s/-b options (see xsel man page).

=head1 EXAMPLE

In B<~/.xbindkeys>:

  "xcleanpaste &"
    Control+Alt + period

  "xcleanpaste -u &"
    Control+Alt + slash

If you copy these three lines:

  This is
     a long URL:
  https://long.url?with=lots&of=arguments

Then press Ctrl+Alt+Period, then paste into some window, you will get:

  This is a long URL: https://long.url?with=lots&of=arguments

If you instead press Ctrl+Alt+Slash, you'll get:

  This is a long URL: https://long.url

=head1 AUTHOR

xcleanpaste was written by B. Watson <yalhcru@gmail.com> and released
under the WTFPL: Do WTF you want with this.

=cut

( $SELF = $0 ) =~ s,.*/,,;

@xselargs = ();
$line_ends = " ";
$url_clean = $verbose = 0;

for(@ARGV) {
	if(/^-?-h(?:elp)/) {
		exec "perldoc $0";
		exit 1; # if the exec failed
	} elsif(/^-v/) {
		$verbose++;
	} elsif(/^-n/) {
		$line_ends = " \\n ";
	} elsif(/^-u/) {
		$url_clean = 1;
	} elsif(/^-e(.*)$/) {
		if(!defined($1) || $1 eq "") {
			die("$SELF: no space allowed between -e option and its argument\n");
		}
		$line_ends = $1;
	} else {
		push @xselargs, $_;
	}
}

$xsel_extra_args = join(" ", @xselargs);
warn "$SELF: xsel arguments: \"$xsel_extra_args\"\n" if $verbose;

# We have to read the entire output of xsel -o into memory before
# spawning the xsel -i, because otherwise the 2nd xsel will obliterate
# the selection buffer the first one's still trying to read from.
open my $in, "xsel -o $xsel_extra_args|" or die $!;
while(<$in>) {
	chomp;
	warn "$SELF: read line $.: $_\n" if $verbose;
	s,\s+$,,;      # remove any trailing whitespace
	s,^\s+,,;      # remove any leading whitespace
	s,[\r\n], ,g;  # replace all CR or LF with a single space
	s,\t,   ,g;    # replace all tabs with 3 spaces
	if($url_clean) {
		# get rid of ? and anything after it, in anything that looks like a URL
		s,(https?:\/\/[^?\s]*)\?\S+,$1,g;
	}
	s/\S$/$&$line_ends/; # our ending LF became a space above; make it
	                     # whatever the line ending is supposed to be
	push @lines, $_;
}
close $in;

$output = join("", @lines);
$output =~ s/\s+$//; # trim trailing whitespace from last line only
warn "$SELF: writing: $output\n" if $verbose;

open my $out, "|xsel -i $xsel_extra_args" or die $!;
print $out $output;
close $out;