[comp.sources.misc] v06i071: m - the more/mail/make/man thing

allbery@uunet.UU.NET (Brandon S. Allbery - comp.sources.misc) (03/22/89)

Posting-number: Volume 6, Issue 71
Submitted-by: tcjones@watdragon.waterloo.edu (speedboat jones)
Archive-name: m

[It's easier to shoot such people on sight -- or "unalias m" as your first
action.  I don't use alias abbreviations, since their interpretation is
always open to question and I have to deal with some 30 systems on a day-
to-day basis, sharing an alias file with at least two other people.  ++bsa]

Here is an extremely silly thing called "m". Isn't frustrating how some
people alias m for mail, others use m for more, and some even use it
for man? So that when you type at their terminal when they have their
backs turned you can never get anything right?

M is a more/mail/make/man thing. You just use m instead of any of those
four and it attempts to figure out what you meant and execute the
appropriate thing for you. It usually gets it right and if not, well,
you can't be hurt too much.  And you can always use the real command.

This, of course, does not solve the above problem, so I suggest that 
everyone who uses this program changes their prompt to
    I-Use-M:
so that the rest of the world will know.

Whatever. Have fun etc. This will need a few changes to work on non-BSD
systems I think.


Here is (sort of) what happens...

You type:             m thinks:
---------------------------------------------
unix_command | m      more

m                     mail (if there is recent mail)... ELSE
                      make (if there is a Makefile or makefile)... ELSE
                      mail

m X                   more X (if X is an ordinary file.) ELSE...
                      mail X (if X is a user name or mail alias). ELSE...
					  make X (if X is a makefile target).
					  man X.

m A, B...             mail A, B...
m x X                 man x X (if x is a manual section and X a manpage)
m -f                  mail -f
m -f X                mail -f X (if X is an ordinary file).
m -k X                man -k X




Terry Jones

    Department Of Computer Science,  University Of Waterloo
    Waterloo Ontario Canada N2L 3G1. Phone: 1-519-8884674
    UUCP:                    ...!watmath!watdragon!tcjones
    CSNET, Internet, CDNnet: tcjones@dragon.waterloo.{cdn,edu}
    BITNET:                  tcjones@WATER.bitnet
    Canadian domain:         tcjones@dragon.uwaterloo.ca

#! /bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of archive 1 (of 1)."
# Contents:  m m/Makefile m/README m/m.c m/tags
# Wrapped by tcjones@watdragon on Wed Mar 15 13:01:48 1989
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test ! -d 'm' ; then
    echo shar: Creating directory \"'m'\"
    mkdir 'm'
fi
if test -f 'm/Makefile' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'m/Makefile'\"
else
echo shar: Extracting \"'m/Makefile'\" \(43 characters\)
sed "s/^X//" >'m/Makefile' <<'END_OF_FILE'
CFLAGS = -O
X
m: m.o
X	cc $(CFLAGS) -o m m.o
END_OF_FILE
if test 43 -ne `wc -c <'m/Makefile'`; then
    echo shar: \"'m/Makefile'\" unpacked with wrong size!
fi
# end of 'm/Makefile'
fi
if test -f 'm/README' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'m/README'\"
else
echo shar: Extracting \"'m/README'\" \(1835 characters\)
sed "s/^X//" >'m/README' <<'END_OF_FILE'
X
Here is an extremely silly thing called "m". Isn't frustrating how some people
alias m for mail, others use m for more, and some even use it for man? So that
when you type at their terminal when they have their backs turned you can never
get anything right? 
X
M is a more/mail/make/man thing. You just use m instead of any of those four
and it attempts to figure out what you meant and execute the appropriate thing
for you. It usually gets it right and if not, well, you can't be hurt too much.
And you can always use the real command.
X
This, of course, does not solve the above problem, so I suggest that everyone
who uses this program changes their prompt to
I-Use-M:
so that the rest of the world will know :-)
X
Whatever. Have fun etc.
X
X
Here is (sort of) what happens...
X
You type:             m thinks:
X---------------------------------------------
unix_command | m      more
X
m                     mail (if there is recent mail)... ELSE
X                      make (if there is a Makefile or makefile)... ELSE
X                      mail
X
m X                   more X (if X is an ordinary file.) ELSE...
X                      mail X (if X is a user name or mail alias). ELSE...
X					  make X (if X is a makefile target).
X					  man X.
X
m A, B...             mail A, B...
m x X                 man x X (if x is a manual section and X a manpage)
m -f                  mail -f
m -f X                mail -f X (if X is an ordinary file).
m -k X                man -k X
X
X
X
X
Terry Jones
X
X    Department Of Computer Science,  University Of Waterloo
X    Waterloo Ontario Canada N2L 3G1. Phone: 1-519-8884674
X    UUCP:                    ...!watmath!watdragon!tcjones
X    CSNET, Internet, CDNnet: tcjones@dragon.waterloo.{cdn,edu}
X    BITNET:                  tcjones@WATER.bitnet
X    Canadian domain:         tcjones@dragon.uwaterloo.ca
END_OF_FILE
if test 1835 -ne `wc -c <'m/README'`; then
    echo shar: \"'m/README'\" unpacked with wrong size!
fi
# end of 'm/README'
fi
if test -f 'm/m.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'m/m.c'\"
else
echo shar: Extracting \"'m/m.c'\" \(8315 characters\)
sed "s/^X//" >'m/m.c' <<'END_OF_FILE'
X/*
X * m.c  --  man/mail/make/more guesswork.
X *
X * Invoke one of the above four by looking at our arguments and deciding
X * which is the appropriate one. Has limitations but is fun...
X *
X * Terry Jones
X *
X * -----------------------------------------------------------------------------
X * Department Of Computer Science, University Of Waterloo
X * Waterloo Ontario Canada N2L 3G1
X *
X * {ihnp4,allegra,decvax,utzoo,utcsri,clyde}!watmath!watdragon!tcjones
X * tcjones@dragon.waterloo.{cdn,edu} tcjones@WATER.bitnet
X * tcjones%watdragon@waterloo.csnet 
X * -----------------------------------------------------------------------------
X */
X
X#include <stdio.h>
X#include <sys/types.h>
X#include <sys/stat.h>
X#include <sys/time.h>
X
X/* Some laziness to make things clearer(?) down below */
X
X#define MORE go(more, ARGS)
X#define MAN  go("man", ARGS)
X#define MAIL go("mail", ARGS)
X#define MAKE go("make", ARGS)
X
X#define BIG 4096
X
X/* More laziness. ARGn means "original argument n"
X *                RARGn means "real (non-option) argument n"
X *
X *  Options are assumed to come first on the command line and to start with '-'
X *
X */
X
X#define ARGS orig_argv
X#define ARG0 (*orig_argv)
X#define ARG1 (*(orig_argv+1))
X#define RARG0 (*argv)
X#define RARG1 (*(argv+1))
X
X/* Sections of the manual that we can recognize. */
X#define MANSECTIONS "0123456789cinopt"
X
X/* Mail is "new" if less than this number of seconds old. */
X#define NEW_MAIL 120
X
extern char *getenv();
extern char *index();
extern FILE *openfile();
X
char *myname;
char *more;
X
main(argc, argv)
int argc;
char **argv;
X{
X    /* Save the original argument list and count. */
X    register char **orig_argv = argv;
X    register int orig_argc = argc;
X
X    myname = *argv;
X
X    argc--;
X    argv++;
X
X    /* Skip option arguments. */
X    while (**argv == '-'){
X        argv++;
X        argc--;
X    }
X
X    if (!(more = getenv("PAGER"))) more = "more";
X
X    /* 
X     * argc now hold the number of non-option arguments, argv points
X     * to the first non-option argument.
X     *
X     * The order in which we do the following tests will make an efficiency
X     * difference - try to do more likely and faster tests first.
X     *
X     */
X
X    if (argc == 0 && isatty(0) == 0)                                    MORE;
X    if (argc == 0 && recent_mail())                                     MAIL;
X    if (argc == 0 && makefile())                                        MAKE;
X    if (argc == 1 && is_ord_file(RARG0))                                MORE;
X    if (argc > 0 && hascomma(RARG0, RARG1))                             MAIL;
X    if (argc == 0)                                                      MAIL;
X    if (argc == 1 && is_userid(RARG0))                                  MAIL;
X    if (argc == 1 && is_mailalias(RARG0))                               MAIL;
X    if (argc == 2 && is_manpage(RARG0, RARG1))                          MAN;
X    if (orig_argc == 2 && is_minus('f', ARG1))                          MAIL;
X    if (orig_argc == 3 && is_minus('f', ARG1) && is_ord_file(RARG0))    MAIL;
X    if (argc == 1 && is_maketarget(RARG0))                              MAKE;
X    if (orig_argc == 3 && is_minus('k', ARG1))                          MAN;
X    if (argc == 1 && is_sysmailalias(RARG0))                            MAIL;
X    if (argc == 1)                                                      MAN;
X    
X
X    /* Give up. */
X    printf("%s: Couldn't decide what to do!\n", myname);
X    exit(1);
X}
X
go(s, args)
char *s;
char **args;
X{
X    /* Exec the command passed in using s, with all the original arguments. */
X
X    register char **tmp;
X    *args = s;
X    /*
X     * We could print the new command but it doesn't seem necessary -
X     * the result should make it obvious what we have done.
X     *
X     *   tmp = args;
X     *   while(**tmp) printf("%s ", *tmp++);
X     *   putc('\n', stdout);
X     */
X
X    execvp(s, args);
X    printf(stderr, "%s: Could not execvp %s\n", myname, s);
X    exit(1);
X}
X
X
is_ord_file(s)
char *s;
X{
X    /* Check to see if "s" is an ordinary file. */
X    /* i.e. not a directory etc. */
X    /* if it is executable and also a maketarget then return 0 & it gets made */
X
X    struct stat sbuf;
X
X    if (stat(s, &sbuf) == -1) return 0;
X    if (sbuf.st_mode & S_IFMT != S_IFREG) return 0;
X    if ((sbuf.st_mode & S_IEXEC) && is_maketarget(s)) return 0;
X    return 1;
X}
X
hascomma(s1, s2)
char *s1;
char *s2;
X{
X    /* 
X     * Check to see if either s1 or s2 has a comma. This would seem to
X     * imply that the arguments are a comma separated list for mail.
X     *
X     */
X
X    return index(s1, ',') != NULL || index(s2, ',') != NULL;
X}
X
makefile()
X{
X    /* Check to see if there is a makefile of some description. */
X
X    struct stat sbuf;
X    return stat("Makefile", &sbuf) == 0 || stat("makefile", &sbuf) == 0;
X}
X
is_minus(flag, s)
char flag;
char *s;
X{
X    /* 
X     * Check if "s" is the option given by the flag "flag".
X     * This could be a #define, and so could lots of the next few things.
X     *
X     * But they're ugly.
X     *
X     */
X
X    return s[0] == '-' && s[1] == flag && s[2] =='\0';
X}
X
X
is_userid(s)
char *s;
X{
X    /* 
X     * Is "s" a userid? 
X     * Look for a '@' or a '!' for a remote person, or
X     * check /u/"s" to find out locally.
X     *
X     */
X
X    struct passwd *getpwnam();
X
X    if (index(s, '!') || index(s, '@')) return 1;
X    return getpwnam(s) != NULL;
X}
X
recent_mail()
X{
X    /* 
X     * See if there is mail which has arrived in the last NEW_MAIL seconds.
X     * This could be much better. It could include a check to see if the
X     * mailbox was touched before you logged on and after your previous
X     * logoff - but who wants to read backwards through /usr/adm/wtmp ??
X     *
X     */
X
X    char mail[BIG];
X    struct stat sbuf;
X    struct timeval tv;
X
X    sprintf(mail, "/usr/spool/mail/%s", getenv("USER"));
X    if (stat(mail, &sbuf) == -1) return 0;
X
X    if (sbuf.st_size == 0) return 0;
X
X    if (gettimeofday(&tv, NULL) == -1){
X        fprintf(stderr, "Could not gettimeofday\n");
X        exit(1);
X    }
X
X    return tv.tv_sec - sbuf.st_mtime <= NEW_MAIL;
X}
X
X
is_manpage(sect, command)
char *sect;
char *command;
X{
X    /* Check to see if there is a man page for "command" in section "sect"  */
X
X    char file[BIG];
X    struct stat sbuf;
X
X    /* Watch here, there could be sections like 3x or 3f etc etc. */
X    if (strlen(sect) == 1 && index(MANSECTIONS, *sect) == NULL) return 0;
X
X    sprintf(file, "/usr/man/man%s/%s.%s", sect, command, sect);
X    return stat(file, &sbuf) == 0;
X}
X
X
is_mailalias(s)
char *s;
X{
X    /* Check ~/.mailrc to see if there is an alias by the name of "s" */
X
X    FILE *f;
X    char line[BIG];
X    int slen = strlen(s);
X
X    sprintf(line, "/u/%s/.mailrc", getenv("USER"));
X    if ((f = openfile(line, "r", 0)) == NULL) return 0;
X
X    while (fgets(line, BIG, f) != NULL){
X        register char *temp;
X
X        if (strncmp(line, "alias", 5)) continue;
X
X        temp = line + 5;
X        while (*temp == ' ' || *temp == '\t') temp++;
X
X        if (strncmp(temp, s, slen) == 0) return 1;
X    }
X    return 0;
X}
X
is_sysmailalias(s)
char *s;
X{
X    /* Is "s" an alias in /usr/lib/aliases? */
X
X    FILE *f;
X    char line[BIG];
X    int slen = strlen(s);
X
X    if ((f = openfile("/usr/lib/aliases", "r", 0)) == NULL) return 0;
X
X    while (fgets(line, BIG, f) != NULL){
X        if (strncmp(line, s, slen) == 0 && line[slen] == ':') return 1;
X    }
X    return 0;
X}
X
X
is_maketarget(s)
char *s;
X{
X    /* Is "s" a target in a makefile? */
X
X    FILE *f;
X    char line[BIG];
X    int slen = strlen(s);
X
X    if ((f = openfile("Makefile", "r", 0)) == NULL)
X        if ((f = openfile("makefile", "r", 0)) == NULL)
X            return 0;
X
X    while (fgets(line, BIG, f) != NULL){
X        if (strncmp(line, s, slen) == 0 && 
X            (line[slen] == ':' || line[slen] == '\t' || line[slen] == ' '))
X
X            return 1;
X    }
X    return 0;
X}
X
X
X#define CLOBBER 1
X
XFILE *
openfile(s,mode,flag)
char *s;
char *mode;
int flag;
X{
X    /* 
X     * fopen() the file whose name is "s".
X     * If mode is write and clobber is 0, don't cloober an existing file.
X     *
X     * flag is clobber mode for "w". 1 = clobber existing file.
X     *
X     */
X
X
X    FILE *fp, *fopen();
X    struct stat buf;
X
X    if (*mode=='w' && flag != CLOBBER && stat(s, &buf) != -1) return NULL;
X    if (!(fp=fopen(s,mode))) return NULL;
X    return fp;
X}
END_OF_FILE
if test 8315 -ne `wc -c <'m/m.c'`; then
    echo shar: \"'m/m.c'\" unpacked with wrong size!
fi
# end of 'm/m.c'
fi
if test -f 'm/tags' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'m/tags'\"
else
echo shar: Extracting \"'m/tags'\" \(451 characters\)
sed "s/^X//" >'m/tags' <<'END_OF_FILE'
Mm	m.c	/^main(argc, argv)$/
go	m.c	/^go(s, args)$/
hascomma	m.c	/^hascomma(s1, s2)$/
is_mailalias	m.c	/^is_mailalias(s)$/
is_maketarget	m.c	/^is_maketarget(s)$/
is_manpage	m.c	/^is_manpage(sect, command)$/
is_minus	m.c	/^is_minus(flag, s)$/
is_ord_file	m.c	/^is_ord_file(s)$/
is_sysmailalias	m.c	/^is_sysmailalias(s)$/
is_userid	m.c	/^is_userid(s)$/
makefile	m.c	/^makefile()$/
openfile	m.c	/^openfile(s,mode,flag)$/
recent_mail	m.c	/^recent_mail()$/
END_OF_FILE
if test 451 -ne `wc -c <'m/tags'`; then
    echo shar: \"'m/tags'\" unpacked with wrong size!
fi
# end of 'm/tags'
fi
echo shar: End of archive 1 \(of 1\).
cp /dev/null ark1isdone
MISSING=""
for I in 1 ; do
    if test ! -f ark${I}isdone ; then
	MISSING="${MISSING} ${I}"
    fi
done
if test "${MISSING}" = "" ; then
    echo You have the archive.
    rm -f ark[1-9]isdone
else
    echo You still need to unpack the following archives:
    echo "        " ${MISSING}
fi
##  End of shell archive.
exit 0