commit - 420c083d295615d49b59ac800cadc4c594c4a334
commit + 043c6bbc3d2cef289b90ff7a8b4837d2e39bd205
blob - /dev/null
blob + 613a510a5f363b036bca0d728c1307e249d3fa77 (mode 644)
--- /dev/null
+++ Makefile
+include config.mk
+
+BIN = rmc
+MAN = $(BIN:=.1)
+
+all:
+ @echo edit config.mk and type \`make install\' to install rmc
+
+install:
+ mkdir -p $(DESTDIR)$(PREFIX)/bin
+ cp $(BIN) $(DESTDIR)$(PREFIX)/bin
+ chmod 755 $(DESTDIR)$(PREFIX)/bin/$(BIN)
+ mkdir -p $(DESTDIR)$(MANPREFIX)/man1
+ cp $(MAN) $(DESTDIR)$(MANPREFIX)/man1
+ chmod 644 $(DESTDIR)$(MANPREFIX)/man1/$(MAN)
+
+uninstall:
+ rm -f $(DESTDIR)$(PREFIX)/bin/$(BIN)
+ rm -f $(DESTDIR)$(MANPREFIX)/man1/$(MAN)
+
+clean:
+ -rm -f *.tar.gz
+
+dist: clean
+ mkdir -p rmc-$(VERSION)
+ cp -rf Makefile config.mk README $(MAN) $(BIN) rmc-$(VERSION)
+ tar cf - rmc-$(VERSION) | gzip >rmc-$(VERSION).tar.gz
+ rm -rf rmc-$(VERSION)
+
+.PHONY: install uninstall clean dist
blob - /dev/null
blob + b205c460e64be80f2da5bfd06a8776e939b8e3d9 (mode 644)
--- /dev/null
+++ README
+rmc is a perl script that executes a program to view, edit, compose or print a
+file, directory or url based on the mailcap and mime.types files. To install,
+simply edit the config.mk file as necessary and run `make install'.
blob - /dev/null
blob + 06e1ad8748934dc390829dcb743c829e6b70f9fb (mode 644)
--- /dev/null
+++ config.mk
+VERSION = 0.0
+
+PREFIX = /usr/local
+MANPREFIX = $(PREFIX)/man
+# Linux
+#MANPREFIX = $(PREFIX)/share/man
blob - /dev/null
blob + 0f84bb064f496c86c4b0373e3660a4de9f0bd7fb (mode 755)
--- /dev/null
+++ rmc
+#!/usr/bin/perl
+
+use strict;
+use warnings;
+use Getopt::Std;
+
+our ($opt_e, $opt_c, $opt_p) = 0;
+
+# usage() prints usage information to stderr and dies.
+sub usage {
+ die "usage: $0 [-ecp] file\n";
+}
+
+# shquote($s) returns string $s with all shell metacharacters quoted.
+sub shquote {
+ my $s = shift(@_);
+ $s =~ s/'/'\\''/g;
+ return "'$s'";
+}
+
+# gettyp ($file, $typfile) attempts to find a type corresponding to $file in
+# mime.types file $typfile. Returns the type string on success, or empty
+# string otherwise.
+#
+# mime.types format:
+# - Empty lines, lines, containing only whitespace and lines whose first
+# non-whitespace character is number-sign (#) are ignored;
+# - The rest contain several whitespace-separated fields.
+# - If the first field contains slash (/), it corresponds to the MIME type, and
+# the others to extensions;
+# - Otherwise, the first field is "URI type", and the others are schemes;
+# - First match is used;
+sub gettyp {
+ my ($file, $typfile) = @_;
+ my $ext = $1 if $file =~ /\.([^.]+)$/;
+ my $sch = $1 if $file =~ /^([^:]+):/;
+ defined $ext || defined $sch or return 0;
+
+ open(FILE, '<', $typfile) or return 0;
+ while (<FILE>) {
+ my ($typ, @fld) = split;
+ for (@fld) {
+ if ((defined $sch && $typ =~ m=^scheme/.*= &&
+ $_ eq $sch) || (defined $ext && $_ eq $ext)) {
+ close FILE;
+ return $typ;
+ }
+ }
+ }
+
+ close FILE;
+ return '';
+}
+
+# docmd($file, $ln, $typ, [$styp]) processes line $ln from the cap file. If
+# $styp is empty, and $typ is not "directory", $file is assumed to be a URL,
+# otherwised it's processed as a normal file. On success executes a command and
+# exits.
+#
+# Differences from RFC 1524:
+# - Named parameters are ignored;
+# - textualnewlines is ignored;
+sub docmd {
+ my ($file, $ln, $typ, $styp) = @_;
+ $ln =~ s/^[ \t]*[^;]+;[ \t]*//; # remove the first field
+
+ my ($quoted, $param, $nparam, $copious, $term, $isfile) = 0;
+ my ($view, $template, $test, $compose, $edit, $print) = '';
+ my $qfile = shquote $file;
+
+ $isfile = 1 unless $typ eq 'scheme';
+
+ local *donefld = sub {
+ my $s = shift(@_);
+ if (!length $view) {
+ $view = $s;
+ } elsif (lc($s) =~ /^needsterminal[ \t]*$/) {
+ $term = 1;
+ } elsif (lc($s) =~ /^copiousoutput[ \t]*$/) {
+ $copious = 1;
+ } elsif ($s =~ /^([^=]*)=(.*)/) {
+ my $name = lc($1);
+ if ($name eq "nametemplate") {
+ $template = $2;
+ } elsif ($name eq "test") {
+ $test = $2;
+ } elsif ($name eq "compose") {
+ $compose = $2;
+ } elsif ($name eq "edit") {
+ $edit = $2;
+ } elsif ($name eq "print") {
+ $print = $2;
+ }
+ }
+ };
+
+ local *expand = sub {
+ my ($ln, $file) = @_;
+ my $s = '';
+ my ($quoted, $param, $nparam) = 0;
+
+ for (my @chars = split(//, shift(@_))) {
+ if ($quoted && !$param) {
+ $s .= $_ unless $nparam;
+ $quoted = 0;
+ } elsif (!$quoted && $_ eq '\\') {
+ $quoted = 1;
+ } elsif ($param) {
+ if ($_ eq '{') {
+ $nparam = 1;
+ } elsif ($_ eq 's') {
+ $s .= $file;
+ } elsif ($typ && $_ eq 't') {
+ $s .= $typ;
+ $s .= "/$styp" if length $styp;
+ }
+ $param = $quoted = 0;
+ } elsif ($nparam) {
+ $nparam = 0 if $_ eq '}';
+ } elsif ($_ eq '%') {
+ $param = 1;
+ } else {
+ $s .= $_;
+ }
+ }
+
+ return $s;
+ };
+
+ local *rmtemp = sub {
+ length $template && $file =~ m=^/tmp/rmc.$$/= or return;
+ unlink $file or warn "$0: can't unlink file $file\n";
+ rmdir "/tmp/rmc.$$" or
+ warn "$0: can't remove directory '/tmp/rmc.$$'\n";
+ };
+
+ my $new = 1;
+ my $s = '';
+ for (my @chars = split(//, $ln)) {
+ next if $new && /[ \t]/;
+ $new = 0;
+
+ if ($quoted) {
+ $quoted = 0;
+ } elsif ($_ eq '\\') {
+ $quoted = 1;
+ } elsif ($_ eq ';') {
+ donefld($s);
+ $s = '';
+ $new = 1;
+ next;
+ }
+ $s .= $_;
+ }
+ donefld($s) unless $ln =~ /;[ \t]*$/;
+
+ return if length $test && system(expand($test, $qfile) .
+ ">/dev/null 2>&1") != 0;
+
+ my $cmd;
+ if ($opt_c) {
+ length $compose or return;
+ $cmd = $compose;
+ } elsif ($opt_e) {
+ length $edit or return;
+ $cmd = $edit
+ } elsif ($opt_p) {
+ length $print or return;
+ $cmd = $print;
+ } else {
+ $cmd = $view;
+ }
+
+ if ($isfile && length $template) {
+ mkdir("/tmp/rmc.$$", 0700) or
+ die "$0: can't create directory /tmp/rmc.$$\n";
+ my $f = "/tmp/rmc.$$/" . expand($template,
+ $file =~ m=([^/]*)$= ? $1 : '');
+ unless (symlink $file =~ m=^/= ? $f : "$ENV{PWD}/$file", $f) {
+ rmdir "/tmp/rmc.$$" or
+ warn "$0: can't remove directory /tmp/rmc.$$";
+ die "$0: can't symlink $file to $f\n";
+ }
+ $file = $f;
+ $qfile = shquote $file;
+ }
+
+ $cmd = expand($cmd, $qfile);
+
+ if ($copious && $term && !$opt_c && !$opt_e && !$opt_p) {
+ my $pager = $ENV{'PAGER'};
+ length $pager or $pager = 'less';
+ $cmd .= "|$pager";
+ }
+
+ my $dofork = 0;
+ if (!$term) {
+ $dofork = 1;
+ } elsif ($opt_p && !(-t && -t STDOUT)) {
+ $dofork = 1;
+ unless (exists $ENV{'DISPLAY'}) {
+ rmtemp();
+ return;
+ }
+ my $terminal = $ENV{'TERMINAL'};
+ length $terminal or $terminal = 'xterm';
+ $cmd = "$terminal -e sh -c " . shquote $cmd;
+ }
+ $cmd .= ">/dev/null 2>&1" if $dofork;
+
+ my $pid;
+ if ($dofork) {
+ $pid = fork;
+ defined $pid or die "fork: $!";
+ }
+ if (!$dofork || !$pid) {
+ system $cmd;
+ rmtemp();
+ }
+ exit;
+}
+
+my $file;
+my ($typ, $styp) = '';
+
+usage if !getopts('ecp') || $#ARGV < 0;
+$file = $ARGV[0];
+
+my $typfile = $ENV{'MIMETYPES'};
+unless (length($typfile)) {
+ my $hometf = "$ENV{HOME}/.mime.types";
+ $typfile = -f $hometf ? $hometf : "/etc/mime.types";
+}
+
+unless ($typ = gettyp $file, $typfile) {
+ if (-d $file) {
+ $typ = 'directory';
+ } else {
+ my $qfile = shquote $file;
+ $typ = lc `file -i $qfile`;
+ }
+}
+
+unless ($typ eq 'directory') {
+ die "bad type $typ\n" unless $typ =~ m=(.*: )?([^/]+)(/[^/]+)=;
+ $typ = $2;
+ $styp = $3;
+ $styp =~ s=^/==;
+}
+
+my $mailcap = "$ENV{HOME}/.mailcap";
+open(FILE, '<', $mailcap) or next;
+my ($cont, $match, $n) = 0;
+my ($rec, $ctyp, $cstyp) = '';
+
+while (my $ln = <FILE>) {
+ chomp $ln;
+ next if $ln =~ /^[ \t]*(#.*)?$/;
+ $n++;
+
+ unless ($cont) {
+ $cont = 1 if $ln =~ /\\$/;
+ if ($ln =~ m=^[ \t]*([^;/]+)(/[^;]+)?;=) {
+ $ctyp = $1;
+ $cstyp = $2;
+ $cstyp =~ s=^/== if length $cstyp;
+ } else {
+ warn "$0: syntax error on line $n of $mailcap\n";
+ next;
+ }
+ $match = 1 if $ctyp eq $typ && (!length $cstyp ||
+ $cstyp eq "*" || $cstyp eq $styp);
+ }
+
+ if ($ln =~ /^(.*)\\$/) {
+ $rec .= "$1 " if $match;
+ $cont = 1;
+ $ln = <FILE>;
+ redo;
+ }
+
+ if ($match) {
+ $rec .= $ln;
+ docmd $file, $rec, $typ, $styp;
+ }
+} continue {
+ $rec = '';
+ $n = $match = $cont = 0;
+}
+
+close FILE;
blob - /dev/null
blob + 80f325c3f15e09358d80738187331d4fd72f4861 (mode 644)
--- /dev/null
+++ rmc.1
+.Dd February 19, 2023
+.Dt RMC 1
+.Os
+.Sh NAME
+.Nm rmc
+.Nd view, edit, compose or print files, directories and URLs.
+.Sh SYNOPSIS
+.Nm
+.Op Fl ecp
+.Ar file
+.Sh DESCRIPTION
+The
+.Nm
+command views, edits, composes or prints a local file, directory or URL.
+In this manual page simply
+.Dq file
+to denote either of the three, while specifically
+.Dq local file
+means a file actually present in the local file hierarchy. Commands to
+execute are chosen from the first matching entry in the mailcap file
+(see
+.Sx the mailcap file ) ,
+based on the local file/directory's MIME type or the URL's scheme. The
+view-command of a matching mailcap entry is executed by default. File
+extensions and URI schemes can be mapped via the mime.types file (see
+.Sx the mime.types file ) .
+If no such mapping is found, directories are automatically recognised to
+have the pseudo-MIME type
+.Dq directory ,
+and normal files are recognised using the
+.Xr file 1
+command.
+.Pp
+The options are as follows:
+.Bl -tag -width Ds
+.It Fl e
+Use the edit-command istead of the view-command.
+.It Fl c
+Use the compose-command istead of the view-command.
+.It Fl p
+Use the print-command istead of the view-command.
+.Be
+.El
+.Ss The mailcap file
+The mailcap file describes commands to execute depending on the file's
+MIME type. Empty lines and lines, whose first non-whitespace character
+is a number sign (#) are ignored. Other lines consist of fields,
+separated by semicolons (;), optionally continued on the next line if
+the last character is a backslash (\\). Backslashes may also be used for
+quoting characters: if a character (including another backslash) is
+preceeded by a backslash, it is stripped of its special meaning, and is
+used literally. Semicolon following the last field is optional.
+.Pp
+There are two types of variables: single-letter variables, and named
+variables. Named variables begin with
+.Dq %{
+and end with
+.Dq } .
+Currently
+.Nm
+simply ignores such strings.
+.Nm
+recognises two single-letter variables:
+.Bl -tag -width Ds
+.Pp
+.It %t
+Is replaced with the file's MIME type.
+.It %s
+Is replaced with the file's name.
+.El
+.Pp
+The first field always contains the MIME type to match against. Subtype
+may be ommited by either only specifying the type, or writing an
+asterisk (*) in subtype's place, in which case the subtype is
+disregarded when matching. For example, consider these fields:
+.Bd -literal -offset indent
+text/*
+text
+text/plain
+.Ed
+.Pp
+The first two fields will match all files with type
+.Dq text
+regardless of the subtype, while the last one will only match files,
+whose MIME type is
+.Dq text/plain .
+.Pp
+The second field is always the view-command. The others are either
+flags or named fields.
+.Pp
+Flag fields are as follows:
+.Bl -tag -width needsterminalx
+.It needsterminal
+If both the standard input and standard output are associated with a
+terminal, the command is unchanged. Otherwise if
+.Nm
+is running under X11, the command is executed in a new terminal.
+Finally, if neither is true, the match fails. Not used when executing
+the print-command.
+.It copiousoutput
+If the view-command is being executed, the output is piped into a
+pager. Only used when needsterminal is specified as well.
+.El
+.Pp
+Named fields contain the name of the field, followed by the equality
+sign (=), followed by the field's value, e.g.:
+.Pp
+.Dl edit=$VISUAL %s
+.Pp
+Following named fields are recognised:
+.Bl -tag -width composex
+.It nametemplate
+If nametemplate is set, before executing the command,
+.Nm
+will create a symlink to file, named as the value of nametemplate. It
+can be useful with programs that expect files to be named in a certain
+way. Only used with local files.
+.It test
+The value of test is a command to be executed before the main command.
+If this commands returns >0, the entry doesn't match.
+.It compose
+The compose-command.
+.It edit
+The edit-command.
+.It print
+The print-command.
+.El
+.Pp
+If there is no named field for the requested command, the mailcap entry
+doesn't match. Unrecognised fields are ignored.
+.Ss The mime.types file
+The mime.types file contains mappings of file extensions and URL schemes
+to MIME types. Empty lines, and lines whose first non-whitespace
+character is the number sign (#) are ignored. Other lines are divided
+into a number of whitespace separated fields. The first field always
+denotes a MIME type. If the type is
+.Dq scheme ,
+the other fields correspond URL schemes; otherwise they denote
+filename extensions.
+.Pp
+For example, given the entries
+.Bd -literal -offset indent
+scheme/http http https
+text/html html htm
+.Ed
+.Pp
+strings starting with
+.Dq http:
+or
+.Dq https:
+will be recognised as scheme/http; otherwise, strings ending with
+.Dq .html
+or
+.Dq .htm
+will be recognised as text/html.
+.Sh ENVIRONMENT
+.Bl -tag -width TERMINALX
+.It Ev TERMINAL
+Terminal command to use. Default is
+.Dq xterm .
+.It Ev PAGER
+Pager command to use. Default is
+.Dq less .
+.El
+.Sh FILES
+.Bl -tag -width /etc/mime.typesx
+.It Pa ~/.mailcap
+The mailcap file.
+.It Pa /etc/mime.types
+System-wide MIME type mappings. Unused if
+.Pa ~/.mime.types
+is present.
+.It Pa ~/.mime.types
+User-specific MIME types mappings.
+.El
+.Sh EXAMPLES
+With the following
+.Pa ~/.mime.types :
+.Bd -literal -offset indent
+scheme/mailto mailto
+text/org org
+.Ed
+.Pp
+and the following
+.Pa ~/.mailcap :
+.Bd -literal -offset indent
+scheme/mailto; guimail %s
+text/org; less %s; needsterminal
+text; cat %s; edit=vi %s; needsterminal
+.Ed
+.Pp
+typing
+.Pp
+.Dl $ rmc mailto:user@site.org
+.Pp
+will execute the GUI program guimail with the argument
+.Dq mailto:user@site.org ,
+while typing
+.Pp
+.Dl $ rmc ./mailto:user@site.org
+.Pp
+will execute
+.Xr less 1
+with the arguments
+.Dq -X
+and
+.Dq ./mailto:user@site.org ,
+and finally, typing
+.Pp
+.Dl $ rmc -e ./mailto:user@site.org
+.Pp
+will execute
+.Xr vi 1
+with the argument
+.Dq ./mailto:user@site.org .
+.Sh EXIT STATUS
+.Ex -std
+.Sh SEE ALSO
+.Xr file 1
+.Sh STANDARDS
+.Rs
+.%A N. Borenstein
+.%D September 1993
+.%R RFC 1524
+.%T A User Agent Configuration Mechanism For Multimedia Mail Format Information
+.Re
+.Sh CAVEATS
+According to RFC 1524 all files in the
+.Ev MAILCAPS
+or
+.Pa ~/.mailcap : Ns Pa /etc/mailcap : Ns Pa /usr/etc/mailcap : Ns Pa /usr/local/etc/mailcap
+path should be searched until a match is found. This behaviour is not
+considered to be useful, intuitive, or secure, so only the
+.Pa ~/.mailcap
+file is searched.
+.Sh AUTHORS
+.An Alexander Arkhipov Aq Mt aa_src@manpager.net .