[alt.sources] File Utility for a Remote Network Site

cristy@eplrx7.uucp (John Cristy) (06/07/90)

#! /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 shell archive."
# Contents:  README xtp.man Makefile xtp.c regular.h regular.c
# Wrapped by cristy@flywheel on Wed Jun  6 13:03:15 1990
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'README' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'README'\"
else
echo shar: Extracting \"'README'\" \(1830 characters\)
sed "s/^X//" >'README' <<'END_OF_FILE'
XXtp is a utility for retrieving, listing, or printing files from a
Xremote network site.  Xtp performs most of the same functions as the
Xftp program, but does not require any interactive commands.  You simply
Xspecify the file transfer task on the command line and xtp performs the
Xtransfer automatically.
X
XTo retrieve file display.tar.Z from host wizard.dupont.com, use:
X
X  xtp -binary -retrieve display.tar.Z wizard.dupont.com
X
XXtp requires 4.3 BSD compatibilities.  I have successfully executed it on
Xa SUN, DEC Ultrix, and Ardent Titan.
X
XThe author is cristy@dupont.com.  Comments, suggestions, etc, are
Xwelcome, but be kind.
X
X---
X
XCopyright 1990 E. I. Dupont de Nemours & Company
X
XPermission to use, copy, modify, distribute, and sell this software and
Xits documentation for any purpose is hereby granted without fee,
Xprovided that the above copyright notice appear in all copies and that
Xboth that copyright notice and this permission notice appear in
Xsupporting documentation, and that the name of E. I. Dupont de Nemours
X& Company not be used in advertising or publicity pertaining to
Xdistribution of the software without specific, written prior
Xpermission.  E. I. Dupont de Nemours & Company makes no representations
Xabout the suitability of this software for any purpose.  It is provided
X"as is" without express or implied warranty.
X
XE. I. Dupont de Nemours & Company disclaims all warranties with regard
Xto this software, including all implied warranties of merchantability
Xand fitness, in no event shall E. I. Dupont de Nemours & Company be
Xliable for any special, indirect or consequential damages or any
Xdamages whatsoever resulting from loss of use, data or profits, whether
Xin an action of contract, negligence or other tortious action, arising
Xout of or in connection with the use or performance of this software.
X
END_OF_FILE
if test 1830 -ne `wc -c <'README'`; then
    echo shar: \"'README'\" unpacked with wrong size!
fi
chmod +x 'README'
# end of 'README'
fi
if test -f 'xtp.man' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'xtp.man'\"
else
echo shar: Extracting \"'xtp.man'\" \(3159 characters\)
sed "s/^X//" >'xtp.man' <<'END_OF_FILE'
X.ad l
X.nh
X.TH XTP 1 "16 April 1990" 
X.SH NAME
Xxtp - file transfer program
X.SH SYNOPSIS
X.B "xtp"
X[ \fI-options\fP ... ] \fI<host/ip address>\fP \fI<home directory>\fP
X.SH DESCRIPTION
X.PP
X.I Xtp
Xis a utility for retrieving, listing, or printing files from a remote 
Xnetwork site.
X.I Xtp
Xperforms most of the same functions as the \fIftp\fP program, but does
Xnot require any interactive commands.  You simply specify the file transfer
Xtask on the command line and \fIxtp\fP performs the transfer automatically.
X.SH EXAMPLE
X.PP
XTo retrieve file display.tar.Z from host wizard.dupont.com, use:
X.PP
Xxtp -binary -retrieve display.tar.Z wizard.dupont.com
X.PP
X.SH OPTIONS
X.TP
X.B "-binary"
Xfetch files as binary.
X.TP
X.B "-exclude \fIexpression\fP"
Xexclude any files that match this full regular expression.
X.TP
X.B "-directory"
Xprint a directory of files.
X.TP
X.B "-ident \fIpassword\fP"
Xspecifies password.
X.TP
X.B "-print \fIexpression\fP"
Xprint any files that match this full regular expression.
X.TP
X.B "-retrieve \fIexpression\fP"
Xretrieve any files that match this full regular expression.
X.TP
X.B "-timeout \fIseconds\fP"
Xspecifies maximum seconds to logon host.
X.TP
X.B "-user \fIname\fP"
Xidentify yourself to the remote FTP server.
X.PP
XIf both \fI-print\fP and \fI-retrieve\fP are not  specified on the command 
Xline, a directory of files is listed for the remote network host.
X.PP
XIf only the program name is specified on the command line, the program command 
Xsyntax and options are listed.  
X.PP
X.SH SEE ALSO
Xftp(1C)
X.SH COPYRIGHT
XCopyright 1990 E. I. Dupont de Nemours & Company                           
X.PP                                                                           
XPermission to use, copy, modify, distribute, and sell this software and    
Xits documentation for any purpose is hereby granted without fee,           
Xprovided that the above copyright notice appear in all copies and that     
Xboth that copyright notice and this permission notice appear in            
Xsupporting documentation, and that the name of E. I. Dupont de Nemours     
X& Company not be used in advertising or publicity pertaining to            
Xdistribution of the software without specific, written prior               
Xpermission.  E. I. Dupont de Nemours & Company makes no representations    
Xabout the suitability of this software for any purpose.  It is provided    
X"as is" without express or implied warranty.                               
X.PP
XE. I. Dupont de Nemours & Company disclaims all warranties with regard     
Xto this software, including all implied warranties of merchantability      
Xand fitness, in no event shall E. I. Dupont de Nemours & Company be        
Xliable for any special, indirect or consequential damages or any           
Xdamages whatsoever resulting from loss of use, data or profits, whether    
Xin an action of contract, negligence or other tortious action, arising     
Xout of or in connection with the use or performance of this software.      
X.SH ACKNOWLEDGEMENTS
XSteve Singles, University of Delaware, for the initial implementation of 
Xthis program.
X.SH AUTHORS
XJohn Cristy, E.I. DuPont De Nemours & Company Incorporated
X
END_OF_FILE
if test 3159 -ne `wc -c <'xtp.man'`; then
    echo shar: \"'xtp.man'\" unpacked with wrong size!
fi
chmod +x 'xtp.man'
# end of 'xtp.man'
fi
if test -f 'Makefile' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'Makefile'\"
else
echo shar: Extracting \"'Makefile'\" \(170 characters\)
sed "s/^X//" >'Makefile' <<'END_OF_FILE'
XCFLAGS= -O
X
Xxtp: 	xtp.o regular.o
X	cc -o $@ xtp.o regular.o 
X
Xinstall:xtp
X	install -c xtp /usr/local/bin
X	install -c xtp.man /usr/man/manl/xtp.l
X
Xclean:
X	/bin/rm xtp *.o
END_OF_FILE
if test 170 -ne `wc -c <'Makefile'`; then
    echo shar: \"'Makefile'\" unpacked with wrong size!
fi
chmod +x 'Makefile'
# end of 'Makefile'
fi
if test -f 'xtp.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'xtp.c'\"
else
echo shar: Extracting \"'xtp.c'\" \(30332 characters\)
sed "s/^X//" >'xtp.c' <<'END_OF_FILE'
X/*
X%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
X%                                                                             %
X%                                                                             %
X%                                                                             %
X%                            X   X  TTTTT PPPP                                %
X%                             X X     T   P   P                               %
X%                              X      T   PPPP                                %
X%                             X X     T   P                                   %
X%                            X   X    T   P                                   %
X%                                                                             %
X%                                                                             %
X%                         File transfer program.                              %
X%                                                                             %
X%                                                                             %
X%                                                                             %
X%                           Software Design                                   %
X%                             John Cristy                                     %
X%                            February 1989                                    %
X%                                                                             %
X%                                                                             %
X%  Copyright 1990 E. I. Dupont de Nemours & Company                           %
X%                                                                             %
X%  Permission to use, copy, modify, distribute, and sell this software and    %
X%  its documentation for any purpose is hereby granted without fee,           %
X%  provided that the above copyright notice appear in all copies and that     %
X%  both that copyright notice and this permission notice appear in            %
X%  supporting documentation, and that the name of E. I. Dupont de Nemours     %
X%  & Company not be used in advertising or publicity pertaining to            %
X%  distribution of the software without specific, written prior               %
X%  permission.  E. I. Dupont de Nemours & Company makes no representations    %
X%  about the suitability of this software for any purpose.  It is provided    %
X%  "as is" without express or implied warranty.                               %
X%                                                                             %
X%  E. I. Dupont de Nemours & Company disclaims all warranties with regard     %
X%  to this software, including all implied warranties of merchantability      %
X%  and fitness, in no event shall E. I. Dupont de Nemours & Company be        %
X%  liable for any special, indirect or consequential damages or any           %
X%  damages whatsoever resulting from loss of use, data or profits, whether    %
X%  in an action of contract, negligence or other tortious action, arising     %
X%  out of or in connection with the use or performance of this software.      %
X%                                                                             %
X%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
X%
X%  Xtp is a utility for retrieving, listing, or printing files from a remote
X%  network site.
X%
X%  This program was adapted from a similiar program written by Steve Singles,
X%  University of Delaware.
X%
X%  Command syntax:
X%
X%  Usage: xtp [-options ...] <host/ip address> [ <home directory> ]
X%  
X%  Where options include:
X%    -binary               retrieve files as binary
X%    -exclude expression   exclude any files that match this expression
X%    -directory            print a directory of files
X%    -ident password       specifies password
X%    -print expression     print any files that match this expression
X%    -retrieve expression  retrieve any files that match this expression
X%    -timeout seconds      specifies maximum seconds to logon host
X%    -user name            identify yourself to the remote FTP server
X%
X%
X*/
X
X/*
X  Include declarations.
X*/
X#include <stdio.h>
X#include <ctype.h>
X#include <signal.h>
X#include <strings.h>
X#include <sys/file.h>
X#include <sys/types.h>
X#include <sys/time.h>
X#include <sys/socket.h>
X#include <sys/wait.h>
X#include "regular.h"
X/*
X  Define declarations.
X*/
X#define False  0
X#define True  1
X/*
X  Variable declarations.
X*/
Xchar
X  *malloc(),
X  *program_name,
X  *sprintf(),
X  *slave_tty[16],
X  *Wait();
X
Xint
X  binary,
X  master;
X
XRegularExpression
X  *directory_expression,
X  *exclude_expression,
X  *print_expression,
X  *retrieve_expression;
X
X/*
X%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
X%                                                                             %
X%                                                                             %
X%                                                                             %
X%   D i r e c t o r y  R e q u e s t                                          %
X%                                                                             %
X%                                                                             %
X%                                                                             %
X%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
X%
X%
X*/
Xvoid DirectoryRequest(fileinfo,filename)
Xchar
X  *fileinfo,
X  *filename;
X{
X  (void) fprintf(stdout,"%s %s\n",fileinfo,filename);
X}
X
X/*
X%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
X%                                                                             %
X%                                                                             %
X%                                                                             %
X%   E r r o r                                                                 %
X%                                                                             %
X%                                                                             %
X%                                                                             %
X%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
X%
X%
X*/
Xvoid Error(error)
Xchar
X  *error;
X{
X  char
X    message[80];
X
X  (void) sprintf(message,"%s: %s",program_name,error);
X  perror(message);
X  exit(1);
X}
X
X/*
X%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
X%                                                                             %
X%                                                                             %
X%                                                                             %
X%   E x e c u t e F t p                                                       %
X%                                                                             %
X%                                                                             %
X%                                                                             %
X%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
X%
X%
X*/
Xvoid ExecuteFtp(hostname)
Xchar
X  *hostname;
X{
X#include <sys/ioctl.h>
X
X  int
X    slave;
X
X  struct ltchars
X    lc;
X
X  struct sgttyb
X    b;
X
X  struct tchars
X    tc;
X
X  (void) signal(SIGTSTP,SIG_IGN);
X  if (isatty(0))
X    {
X      int
X        tty;
X
X      tty=open("/dev/tty",O_RDWR);
X      if (tty >= 0)
X        {
X          (void) ioctl(tty,TIOCNOTTY,(char *) 0);
X          (void) close(tty);
X        }
X    }
X  slave=open((char *) slave_tty,O_RDWR);
X  if (slave < 0)
X    Error((char *) slave_tty);
X  /*
X    Fix tty line.
X  */
X  (void) ioctl(slave,TIOCGETP,&b);
X  b.sg_flags&=~(ECHO | CRMOD);
X  b.sg_erase=(-1);
X  b.sg_kill=(-1);
X  (void) ioctl(slave,TIOCSETP,&b);
X  tc.t_intrc=(-1);
X  tc.t_quitc=(-1);
X  tc.t_startc=(-1);
X  tc.t_stopc=(-1);
X  tc.t_eofc=(-1);
X  tc.t_brkc=(-1);
X  (void) ioctl(slave,TIOCSETC,&tc);
X  lc.t_suspc=(-1);
X  lc.t_dsuspc=(-1);
X  lc.t_rprntc=(-1);
X  lc.t_flushc=(-1);
X  lc.t_werasc=(-1);
X  lc.t_lnextc=(-1);
X  (void) ioctl(slave,TIOCSLTC,&lc);
X  (void) close(master);
X  (void) dup2(slave,0);
X  (void) dup2(slave,1);
X  (void) dup2(slave,2);
X  (void) close(slave);
X  (void) execl("/usr/ucb/ftp","ftp","-n","-i","-g",hostname,(char *) 0);
X  perror("/usr/ucb/ftp");
X  (void) kill(0,SIGTERM);
X}
X
X/*
X%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
X%                                                                             %
X%                                                                             %
X%                                                                             %
X%   F e t c h R e q u e s t                                                   %
X%                                                                             %
X%                                                                             %
X%                                                                             %
X%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
X%
X%
X*/
Xvoid FetchRequest(filename)
Xchar
X  *filename;
X{
X  char
X    command[256],
X    *p,
X    *response;
X
X  /*
X    get remote-file local-file
X  */
X  p=filename+strlen(filename);
X  while ((p > filename) && (*--p != '/'));
X  if (*p == '/')
X    p++;
X  (void) sprintf(command,"get %s %s\n",filename,p);
X  (void) write(master,command,strlen(command));
X  (void) fprintf(stdout,"%s retrieved.\n",filename);
X  while (response=Wait())
X    (void) fprintf(stdout,"%s\n",response);
X}
X
X/*
X%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
X%                                                                             %
X%                                                                             %
X%                                                                             %
X%   G e t H o s t I n f o                                                     %
X%                                                                             %
X%                                                                             %
X%                                                                             %
X%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
X%
X%
X*/
Xvoid GetHostInfo(host)
Xchar
X  *host;
X{
X#include <netinet/in.h>
X#include <netdb.h>
X#include <arpa/inet.h>
X
X  char
X    *address,
X    *inet_ntoa(),
X    *p;
X
X  struct in_addr
X    in;
X
X  struct hostent
X    *hp;
X
X  if (isdigit(*host))
X    {
X      /*
X         Internet address to name.
X      */
X      in.s_addr=inet_addr(host);
X      hp=gethostbyaddr(&in.s_addr,sizeof(in.s_addr),AF_INET);
X      if (hp != (struct hostent *) NULL)
X        {
X          hp=gethostbyname(hp->h_name);
X          if (hp != (struct hostent *) NULL)
X            {
X              in.s_addr= *(int *) hp->h_addr;
X              address=inet_ntoa(in);
X            }
X        }
X    }
X  else
X    {
X      /*
X         Internet name to address.
X      */
X      hp=gethostbyname(host);
X      if (hp != (struct hostent *) NULL)
X        {
X          in.s_addr= *(int *) hp->h_addr;
X          address=inet_ntoa(in);
X          hp=gethostbyaddr(&in.s_addr,sizeof(in.s_addr),AF_INET);
X        }
X    }
X  if (hp == (struct hostent *) NULL)
X    (void) fprintf(stdout,"%s: ",host);
X  else
X    {
X      p=hp->h_name;
X      while (*p)
X      {
X        if (isupper(*p))
X          *p=tolower(*p);
X        p++;
X      }
X      (void) fprintf(stdout,"%s [%s]: ",hp->h_name,address);
X    }
X}
X
X/*
X%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
X%                                                                             %
X%                                                                             %
X%                                                                             %
X%   G e t P s e u d o T e r m i n a l                                         %
X%                                                                             %
X%                                                                             %
X%                                                                             %
X%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
X%
X%
X*/
Xvoid GetPseudoTerminal()
X{
X#include <sys/stat.h>
X
X  char
X    *master_tty[16];
X
X  register char
X    *bank,
X    *cp;
X
X  struct stat
X    info;
X
X  for (bank="pqrs"; *bank; bank++)
X  {
X    (void) sprintf((char *) master_tty,"/dev/pty%c0",*bank);
X    if (stat(master_tty,&info) < 0)
X      break;
X    for (cp="0123456789abcdef"; *cp; cp++)
X    {
X      (void) sprintf((char *) master_tty,"/dev/pty%c%c",*bank,*cp);
X      master=open((char *) master_tty,O_RDWR);
X      if (master >= 0)
X        {
X          /*
X            Verify slave side is usable.
X          */
X          (void) sprintf((char *) slave_tty,"/dev/tty%c%c",*bank,*cp);
X          if (access((char *) slave_tty,R_OK | W_OK) == 0)
X            return;
X          (void) close(master);
X        }
X    }
X  }
X  Error("All network ports in use.\n");
X}
X
X/*
X%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
X%                                                                             %
X%                                                                             %
X%                                                                             %
X%   P r i n t R e q u e s t                                                   %
X%                                                                             %
X%                                                                             %
X%                                                                             %
X%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
X%
X%
X*/
Xvoid PrintRequest(filename)
Xchar
X  *filename;
X{
X  char
X    command[256],
X    *response;
X
X  /*
X    get remote-file [ - | <| zcat> ].
X  */
X  (void) sprintf(command,"get %s",filename);
X  (void) write(master,command,strlen(command));
X  if (strcmp(filename+strlen(filename)-2,".Z"))
X    (void) sprintf(command," -\n");
X  else
X    (void) sprintf(command," | zcat\n");
X  (void) write(master,command,strlen(command));
X  (void) fprintf(stdout,"%s:\n",filename);
X  while (response=Wait())
X    (void) fprintf(stdout,"%s\n",response);
X}
X
X/*
X%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
X%                                                                             %
X%                                                                             %
X%                                                                             %
X%   P r o c e s s R e q u e s t                                               %
X%                                                                             %
X%                                                                             %
X%                                                                             %
X%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
X%
X%
X*/
Xvoid ProcessRequest()
X{
X  typedef struct _DirectoryNode
X  {
X    char
X      *info,
X      *name;
X
X    struct _DirectoryNode
X      *next;
X  } DirectoryNode;
X
X  char
X    command[256],
X    directory[1024],
X    *info,
X    *name,
X    *response;
X
X  DirectoryNode
X    *next,
X    *root;
X
X  int
X    unix_filesystem;
X
X  register char
X    *p;
X
X  register DirectoryNode
X    **last,
X    *node;
X
X  RegularExpression
X    *expression;
X
X  /*
X    Initialize function variables.
X  */
X  root=(DirectoryNode *) NULL;
X  last=(&root);
X  *directory=(char) NULL;
X  unix_filesystem=False;
X  /*
X    Unix-style filesystem if the first character is a '-' or last is '/'.
X  */
X  (void) strcpy(command,"dir\n");
X  (void) write(master,command,strlen(command));
X  response=Wait();
X  if (response == (char *) NULL)
X    return;
X  expression=CompileRegularExpression("[dbclps-][rwx-][rwx-][rwx-]");
X  while (response=Wait())
X  {
X    if (*response == (char) NULL)
X      continue;
X    if (ExecuteRegularExpression(expression,response))
X      unix_filesystem=True; 
X  }
X  free((char *) expression);
X  /*
X    Issue directory command to ftp program.
X  */
X  if (unix_filesystem)
X    (void) strcpy(command,"dir -R\n");
X  else
X    (void) strcpy(command,"dir [...]\n");
X  (void) write(master,command,strlen(command));
X  response=Wait();
X  if (response == (char *) NULL)
X    return;
X  response=Wait();
X  if (response == (char *) NULL)
X    {
X      /*
X        Directory command has limited functionality.
X      */
X      (void) strcpy(command,"dir\n");
X      (void) write(master,command,strlen(command));
X      response=Wait();
X      if (response == (char *) NULL)
X        return;
X    }
X  expression=CompileRegularExpression("Permission denied|not found");
X  if (!unix_filesystem)
X    while (response=Wait())
X    {
X      /*
X        Link non unix-style file into file list.
X      */
X      while (*response == ' ')
X        response++;
X      if (*response == (char) NULL)
X        continue;
X      p=response;
X      while ((*p != ' ') && *p)
X        p++;
X      if (*p)
X        {
X          /*
X            Extract file info.
X          */
X          *p++=(char) NULL;
X          while (*p == ' ')
X            p++;
X        }
X      info=(char *) malloc((unsigned int) (strlen(p)+1));
X      name=(char *) malloc((unsigned int) (strlen(response)+1));
X      if ((info == (char *) NULL) || (name == (char *) NULL))
X        {
X          (void) fprintf(stderr,"Can't continue, not enough memory\n");
X          exit(1);
X        }
X      (void) strcpy(info,p);
X      (void) strcpy(name,response);
X      if (exclude_expression)
X        if (ExecuteRegularExpression(exclude_expression,name))
X          {
X            (void) free(name);
X            (void) free(info);
X            continue;
X          }
X      node=(DirectoryNode *) malloc(sizeof(DirectoryNode));
X      if (node == (DirectoryNode *) NULL)
X        {
X          (void) fprintf(stderr,"Can't continue, not enough memory\n");
X          exit(1);
X        }
X      node->name=name;
X      node->info=info;
X      node->next=(DirectoryNode *) NULL;
X      *last=node;
X      last=(&node->next);
X    }
X  else
X    while (response=Wait())
X    {
X      /*
X         Link unix-style file into file list.
X      */
X      while (*response == ' ')
X        response++;
X      if (*response == (char) NULL)
X        continue;
X      p=response+strlen(response)-1;
X      if (*response == '-')
X        {
X          if (ExecuteRegularExpression(expression,response))
X            continue;
X          /*
X             Extract file name;  assume date followed by file name.
X          */
X          response++;
X          while (p > response)
X            if (*p-- == ' ')
X              if (isdigit(*p))
X                break;
X          while (*++p == ' ');
X          p--;
X          *p++=(char) NULL;
X          info=(char *) malloc((unsigned int) (strlen(response)+1));
X          name=(char *) malloc((unsigned int) (strlen(directory)+strlen(p)+1));
X          if ((info == (char *) NULL) || (name == (char *) NULL))
X            {
X              (void) fprintf(stderr,"Can't continue, not enough memory\n");
X              exit(1);
X            }
X          (void) strcpy(info,response);
X          (void) strcpy(name,directory);
X          (void) strcat(name,p);
X          if (exclude_expression)
X            if (ExecuteRegularExpression(exclude_expression,name))
X              {
X                (void) free(name);
X                (void) free(info);
X                continue;
X              }
X          node=(DirectoryNode *) malloc(sizeof(DirectoryNode));
X          if (node == (DirectoryNode *) NULL)
X            {
X              (void) fprintf(stderr,"Can't continue, not enough memory\n");
X              exit(1);
X            }
X          node->name=name;
X          node->info=info;
X          node->next=(DirectoryNode *) NULL;
X          *last=node;
X          last=(&node->next);
X        }
X      else
X        if (*p == ':')
X          {
X            /*
X              File is a directory.
X            */
X            *p='/';
X            (void) strcpy(directory,response);
X          }
X      }
X  free((char *) expression);
X  /*
X    Traverse file list.
X  */
X  node=root;
X  while (node)
X  {
X    if (directory_expression)
X      if (ExecuteRegularExpression(directory_expression,node->name))
X        (void) DirectoryRequest(node->info,node->name);
X    if (retrieve_expression)
X      if (ExecuteRegularExpression(retrieve_expression,node->name))
X        (void) FetchRequest(node->name);
X    if (print_expression)
X      if (ExecuteRegularExpression(print_expression,node->name))
X        (void) PrintRequest(node->name);
X    /*
X      Free allocated memory for this node.
X    */
X    (void) free(node->info);
X    (void) free(node->name);
X    next=node->next;
X    (void) free((char *) node);
X    node=next;
X  }
X}
X 
X/*
X%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
X%                                                                             %
X%                                                                             %
X%                                                                             %
X%   S i g n a l C h i l d                                                     %
X%                                                                             %
X%                                                                             %
X%                                                                             %
X%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
X%
X%
X*/
Xvoid SignalChild()
X{
X  char
X    message[256];
X
X  int
X    status;
X
X  (void) sprintf(message,"child died, status %x",wait(&status));
X  Error(message);
X}
X
X/*
X%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
X%                                                                             %
X%                                                                             %
X%                                                                             %
X%   U s a g e                                                                 %
X%                                                                             %
X%                                                                             %
X%                                                                             %
X%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
X%
X%  Procedure Usage displays the program usage;
X%
X%  The format of the Usage routine is:
X%
X%      Usage(message)
X%
X%  A description of each parameter follows:
X%
X%    o message:  Specifies a specific message to display to the user.
X%
X%
X*/
Xvoid Usage(message)
Xchar
X  *message;
X{
X  char
X    **p;
X
X  static char
X    *options[]=
X    {
X      "-binary               retrieve files as binary",
X      "-exclude expression   exclude any files that match this expression",
X      "-directory            print a directory of files",
X      "-ident password       specifies password",
X      "-print expression     print any files that match this expression",
X      "-retrieve expression  retrieve any files that match this expression",
X      "-timeout seconds      specifies maximum seconds to logon host",
X      "-user name            identify yourself to the remote FTP server",
X      "-verbose              show all responses from the remote server",
X      NULL
X    };
X  if (message)
X    (void) fprintf(stderr,"Can't continue, %s\n\n",message);
X  (void) fprintf(stderr,
X    "Usage: %s [-options ...] <host/ip address> [ <home directory> ]\n",
X    program_name);
X  (void) fprintf(stderr,"\nWhere options include:\n");
X  for (p=options; *p; *p++)
X    (void) fprintf(stderr,"  %s\n",*p);
X  exit(1);
X}
X
X/*
X%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
X%                                                                             %
X%                                                                             %
X%                                                                             %
X%   W a i t                                                                   %
X%                                                                             %
X%                                                                             %
X%                                                                             %
X%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
X%
X%
X*/
Xchar *Wait()
X{
X  register char
X    *p;
X
X  static char
X    buffer[1024],
X    *q;
X
X  static char
X    line[1024];
X
X  static int
X    count=0;
X
X  p=line;
X  do
X  {
X    if (count <= 0) 
X      {
X        count=read(master,buffer,sizeof(buffer));
X        q=buffer;
X        if (count <= 0)
X          {
X            if (p == line)
X              return((char *) NULL);
X            break;
X          }
X      }
X    count--;
X    *p=(*q++);
X    if (*p == '\n')
X      break;
X    p++;
X    if ((p-line) >= 5)
X      if (!strncmp(p-5,"ftp> ",5))
X        if (count == 0)
X          return((char *) NULL);
X  } while (p < (line+sizeof(line)));
X  *p=(char) NULL;
X  return(line);
X}
X
X/*
X%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
X%                                                                             %
X%                                                                             %
X%                                                                             %
X%   m a i n                                                                   %
X%                                                                             %
X%                                                                             %
X%                                                                             %
X%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
X%
X%
X*/
Xmain(argc,argv)
Xint
X  argc;
X
Xregister char
X  **argv;
X{
X  char
X    command[256],
X    *ident,
X    *home_directory,
X    *hostname,
X    *user;
X
X  int
X    child,
X    status;
X
X  register char
X    *p;
X
X  unsigned int
X    timeout,
X    verbose;
X
X  /*
X    Initialize program variables.
X  */
X  binary=False;
X  directory_expression=(RegularExpression *) NULL;
X  exclude_expression=(RegularExpression *) NULL;
X  ident=(char *) NULL;
X  print_expression=(RegularExpression *) NULL;
X  retrieve_expression=(RegularExpression *) NULL;
X  timeout=0;
X  user="anonymous";
X  program_name=argv[0];
X  verbose=False;
X  /*
X    Parse command line arguments.
X  */
X  for (p=(*argv++); *argv && (**argv == '-'); argv++)
X    switch (argv[0][1])
X    {
X      case 'b':
X      {
X        binary++;
X        break;
X      }
X      case 'd':
X      {
X        directory_expression=CompileRegularExpression(*++argv);
X        if (!directory_expression)
X          exit(1);
X        break;
X      }
X      case 'e':
X      {
X        exclude_expression=CompileRegularExpression(*++argv);
X        if (!exclude_expression)
X          exit(1);
X        break;
X      }
X      case 'i':
X      {
X        ident=(*++argv);
X        break;
X      }
X      case 'p':
X      {
X        print_expression=CompileRegularExpression(*++argv);
X        if (!print_expression)
X          exit(1);
X        break;
X      }
X      case 'r':
X      {
X        retrieve_expression=CompileRegularExpression(*++argv);
X        if (!retrieve_expression)
X          exit(1);
X        break;
X      }
X      case 't':
X      {
X        timeout=atoi(*++argv);
X        break;
X      }
X      case 'u':
X      {
X        user=(*++argv);
X        break;
X      }
X      case 'v':
X      {
X        verbose++;
X        break;
X      }
X      default:
X      {
X        Usage((char *) NULL);
X        break;
X      }
X    }
X  if ((argc < 2) || (*argv == (char *) NULL))
X    Usage((char *) NULL);
X  hostname=argv[0];
X  home_directory=argv[1];
X  if (!directory_expression && !retrieve_expression && !print_expression)
X    directory_expression=CompileRegularExpression("");
X  if (ident == (char *) NULL)
X    {
X      static char
X        name[256];
X
X      /*
X        Identify user as user@host.domain.
X      */
X      (void) cuserid(name);
X      p=name+strlen(name);
X      *p++='@';
X      (void) gethostname(p,64);
X      while (*p)
X        p++;
X      (void) getdomainname(p,64);
X      ident=name;
X    }
X  (void) GetHostInfo(hostname);
X  if (!home_directory)
X    (void) fprintf(stdout,"\n");
X  else
X    (void) fprintf(stdout,"%s\n",home_directory);
X  (void) GetPseudoTerminal();
X  /*
X    Connect and logon to host.
X  */
X  (void) signal(SIGCHLD,SignalChild);
X  if (timeout > 0)
X    (void) alarm(timeout);  /* enable timer. */
X  child=fork();
X  if (child < 0)
X    Error("fork");
X  if (child == 0)
X    ExecuteFtp(hostname);
X  while (p=Wait())
X    (void) fprintf(stderr,"%s\n",p);
X  (void) sprintf(command,"user %s %s\n",user,ident);
X  (void) write(master,command,strlen(command));
X  while (p=Wait())
X    (void) fprintf(stderr,"%s\n",p);
X  if (timeout > 0)
X    (void) alarm(0);  /* disable timer. */
X  (void) fprintf(stderr,"\n");
X  if (!verbose)
X    {
X      (void) strcpy(command,"verbose off\n");
X      (void) write(master,command,strlen(command));
X      while (Wait()); 
X    }
X  if (home_directory)
X    {
X      /*
X        Change remote working directory.
X      */
X      (void) sprintf(command,"cd %s\n",home_directory);
X      (void) write(master,command,strlen(command));
X      while (Wait()); 
X      (void) strcpy(command,"pwd\n");
X      (void) write(master,command,strlen(command));
X      while (p=Wait())
X        (void) fprintf(stderr,"%s\n",p);
X    }
X  if (binary)
X    {
X      /*
X        Set file transfer type.
X      */
X      (void) strcpy(command,"type binary\n");
X      (void) write(master,command,strlen(command));
X      while (Wait()); 
X      (void) write(master,command,strlen(command));
X      (void) strcpy(command,"type\n");
X      while (p=Wait())
X        (void) fprintf(stderr,"%s\n",p);
X    }
X  (void) strcpy(command,"runique\n");
X  (void) write(master,command,strlen(command));
X  while (Wait()); 
X  ProcessRequest();
X  (void) strcpy(command,"quit\n");
X  (void) write(master,command,strlen(command));
X  (void) signal(SIGCHLD,SIG_DFL);
X  /*
X    Wait for child to finish.
X  */
X  while (child != wait(&status));
X  (void) close(master);
X  free((char *) directory_expression);
X  free((char *) exclude_expression);
X  free((char *) print_expression);
X  free((char *) retrieve_expression);
X  return(0);
X}
END_OF_FILE
if test 30332 -ne `wc -c <'xtp.c'`; then
    echo shar: \"'xtp.c'\" unpacked with wrong size!
fi
chmod +x 'xtp.c'
# end of 'xtp.c'
fi
if test -f 'regular.h' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'regular.h'\"
else
echo shar: Extracting \"'regular.h'\" \(1245 characters\)
sed "s/^X//" >'regular.h' <<'END_OF_FILE'
X/*
X  Definitions etc. for RegularExpression(3) routines.
X*/
X#define EndOfProgram  0
X#define MatchBeginningOfLine  1
X#define MatchEndOfProgramOfLine  2
X#define MatchAnyCharacter  3
X#define MatchAnyCharacterOf  4
X#define MatchAnyCharacterBut  5
X#define MatchThisOrNext  6
X#define Back  7
X#define MatchExactly  8
X#define MatchEmptyString  9
X#define MatchZeroOrMore  10
X#define MatchOneOrMore  11
X#define Open  20
X#define Close  30
X
X#define WorstCase  0
X#define NonNull  1
X#define Simple  2
X#define SpecialStart  4
X
X#define Fail(m)  \
X{  \
X  (void) fprintf(stderr,"RegularExpression: %s\n",m);  \
X  return(NULL);  \
X}
X#define Magick   0234
X#define Meta  "^$.[()|?+*\\"
X#define MultipleMatches(c) (((c) == '*') || ((c) == '+') || ((c) == '?'))
X#define Next(p) (((*((p)+1) & 0377) << 8 )+(*((p)+2) & 0377))
X#define NumberSubExpressions  10
X#define OpCode(p) (*(p))
X#define Operand(p) ((p)+3)
X
Xtypedef struct _RegularExpression 
X{
X  char 
X    *subpattern[NumberSubExpressions],
X    *subpattern_end[NumberSubExpressions],
X    start_character,
X    anchor,
X    *priority_pattern;
X
X  int 
X    pattern_length;
X
X  char 
X    program[1];
X} RegularExpression;
X
Xextern RegularExpression 
X  *CompileRegularExpression();
X
Xextern int 
X  ExecuteRegularExpression();
END_OF_FILE
if test 1245 -ne `wc -c <'regular.h'`; then
    echo shar: \"'regular.h'\" unpacked with wrong size!
fi
chmod +x 'regular.h'
# end of 'regular.h'
fi
if test -f 'regular.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'regular.c'\"
else
echo shar: Extracting \"'regular.c'\" \(33286 characters\)
sed "s/^X//" >'regular.c' <<'END_OF_FILE'
X/*
X%  A regular expression is zero or more branches, separated by `|'. It
X%  matches anything that matches one of the branches.
X%  
X%  A branch is zero or more pieces, concatenated. It matches a match for
X%  the first, followed by a match for the second, etc.
X%  
X%  A piece is an atom possibly followed by `*', `+', or `?'. An atom
X%  followed by `*' matches a sequence of 0 or more matches of the atom. An
X%  atom followed by `+' matches a sequence of 1 or more matches of the
X%  atom. An atom followed by `?' matches a match of the atom, or the null
X%  pattern.
X%  
X%  An atom is a regular expression in parentheses (matching a match for
X%  the regular expression), a range (see below), `.' (matching any single
X%  character), `^' (matching the null pattern at the beginning of the input
X%  pattern), `$' (matching the null pattern at the end of the input pattern),
X%  a `\' fol- lowed by a single character (matching that character), or a
X%  single character with no other significance (matching that character).
X%  
X%  A range is a sequence of characters enclosed in `[]'. It normally
X%  matches any single character from the sequence. If the sequence begins
X%  with `^', it matches any single charac- ter not from the rest of the
X%  sequence. If two characters in the sequence are separated by `-', this
X%  is shorthand for the full list of ASCII characters between them (e.g.
X%  `[0-9]' matches any decimal digit). To include a literal `]' in the
X%  sequence, make it the first character (following a possible `^').  To
X%  include a literal `-', make it the first or last character.
X%  
X%  If a regular expression could match two different parts of the input
X%  pattern, it will match the one which begins earliest. If both begin in
X%  the same placebut match dif- ferent lengths, or match the same length
X%  in different ways, life gets messier, as follows.
X%  
X%  In general, the possibilities in a list of branches are con- sidered in
X%  left-to-right order, the possibilities for `*', `+', and `?' are
X%  considered longest-first, nested constructs are considered from the
X%  outermost in, and concatenated con- structs are considered
X%  leftmost-first. The match that will be chosen is the one that uses the
X%  earliest possibility in the first choice that has to be made. If there
X%  is more than one choice, the next will be made in the same manner
X%  (earli- est possibility) subject to the decision on the first choice.
X%  And so forth.
X%  
X%  For example, `(ab|a)b*c' could match `abc' in one of two ways. The
X%  first choice is between `ab' and `a'; since `ab' is earlier, and does
X%  lead to a successful overall match, it is chosen. Since the `b' is
X%  already spoken for, the `b*' must match its last possibility-the empty
X%  pattern-since it must respect the earlier choice.
X%  
X%  In the particular case where no `|'s are present and there is only one
X%  `*', `+', or `?', the net effect is that the longest possible match
X%  will be chosen. So `ab*', presented with `xabbbby', will match `abbbb'.
X%  Note that if `ab*' is tried against `xabyabbbz', it will match `ab'
X%  just after `x', due to the begins-earliest rule. (In effect, the deci-
X%  sion on where to start the match is the first choice to be made, hence
X%  subsequent choices must respect it even if this leads them to
X%  less-preferred alternatives.)
X%  
X%  CompileRegularExpression returns NULL for a failure, where failures are
X%  syntax errors, exceeding implementation limits, or applying `+' or `*'
X%  to a possibly-null operand.
X%
X%  This is essentially the same routine written and copywrited by Henry 
X%  Spencer, University of Toronto.  I made minor programming changes but 
X%  major variable name changes to improve readibility.
X%
X%
X*/
X#include <stdio.h>
X#include <ctype.h>
X#include <malloc.h>
X#include "regular.h"
X
Xchar
X  *code,
X  **subpattern_end,
X  *p,
X  start_code,
X  *start_pattern,
X  **subpattern;
X
Xextern char
X  *strchr();
X
Xstatic char    
X  *token;  
X
Xstatic int      
X  number_parenthesis;  
X
Xstatic long     
X  code_size;  
X
X/*
X%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
X%                                                                             %
X%                                                                             %
X%                                                                             %
X%   A t o m                                                                   %
X%                                                                             %
X%                                                                             %
X%                                                                             %
X%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
X%
X%
X*/
Xstatic char *Atom(flagp)
Xint
X  *flagp;
X{
X  char
X    *Node(),
X    *Regular();
X
X  int
X    flags;
X
X  register char
X    *status;
X
X  void
X    EmitCode();
X
X  *flagp=WorstCase;
X  switch(*token++)
X  {
X    case '^':
X    {
X      status=Node(MatchBeginningOfLine);
X      break;
X    }
X    case '$':
X    {
X      status=Node(MatchEndOfProgramOfLine);
X      break;
X    }
X    case '.':
X    {
X      status=Node(MatchAnyCharacter);
X      *flagp|=NonNull | Simple;
X      break;
X    }
X    case '[':
X    {
X      register int
X        class,
X        class_end;
X
X      if (*token != '^')
X        status=Node(MatchAnyCharacterOf);
X      else
X        {
X          /*
X            Complement of range.
X          */
X          status=Node(MatchAnyCharacterBut);
X          token++;
X        }
X      if ((*token == ']') || (*token == '-'))
X        EmitCode(*token++);
X      while ((*token != '\0') && (*token != ']'))
X      {
X        if (*token != '-')
X          EmitCode(*token++);
X         else
X          {
X            token++;
X            if ((*token == ']') || (*token == '\0'))
X              EmitCode('-');
X            else
X              {
X                class=((int)*(unsigned char *)(token-2))+1;
X                class_end=((int)*(unsigned char *)(token));
X                if (class > class_end+1)
X                  Fail("invalid [] range");
X                for(; class <= class_end; class++)
X                  EmitCode(class);
X                token++;
X              }
X          }
X      }
X      EmitCode('\0');
X      if (*token != ']')
X        Fail("unmatched []");
X      token++;
X      *flagp|=NonNull | Simple;
X      break;
X    }
X    case '(':
X    {
X      status=Regular(1,&flags);
X      if (status == NULL)
X        return(NULL);
X      *flagp|=flags & (NonNull | SpecialStart);
X      break;
X    }
X    case '\0':
X    case '|':
X    case ')':
X    {
X      Fail("internal urp");
X      break;
X    }
X    case '?':
X    case '+':
X    case '*':
X    {
X      Fail("?+* follows nothing");
X      break;
X    }
X    case '\\':
X    {
X      if (*token == '\0')
X        Fail("trailing \\");
X      status=Node(MatchExactly);
X      EmitCode(*token++);
X      EmitCode('\0');
X      *flagp|=NonNull | Simple;
X      break;
X    }
X    default:
X    {
X      register char
X        ender;
X
X      register int
X        length;
X
X      token--;
X      length=strcspn(token,Meta);
X      if (length <= 0)
X        Fail("internal disaster");
X      ender=(*(token+length));
X      if (length > 1 && MultipleMatches(ender))
X        length--;
X      *flagp|=NonNull;
X      if (length == 1)
X        *flagp|=Simple;
X      status=Node(MatchExactly);
X      while (length > 0)
X      {
X        EmitCode(*token++);
X        length--;
X      }
X      EmitCode('\0');
X      break;
X    }
X  }
X  return(status);
X}
X
X/*
X%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
X%                                                                             %
X%                                                                             %
X%                                                                             %
X%   B r a n c h                                                               %
X%                                                                             %
X%                                                                             %
X%                                                                             %
X%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
X%
X%  Function Branch Implements the | operator.
X%
X%
X*/
Xstatic char *Branch(flagp)
Xint
X  *flagp;
X{
X  char
X    *Node(),
X    *Piece();
X
X  int
X    flags;
X
X  register char
X    *chain,
X    *latest,
X    *status;
X
X  void
X    Tail();
X
X  *flagp=WorstCase;
X  status=Node(MatchThisOrNext);
X  chain=NULL;
X  while ((*token != '\0') && (*token != '|') && (*token != ')'))
X  {
X    latest=Piece(&flags);
X    if (latest == NULL)
X      return(NULL);
X    *flagp|=flags & NonNull;
X    if (chain == NULL)
X      *flagp|=flags & SpecialStart;
X    else
X      Tail(chain,latest);
X    chain=latest;
X  }
X  if (chain == NULL)
X   (void) Node(MatchEmptyString);
X  return(status);
X}
X
X/*
X%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
X%                                                                             %
X%                                                                             %
X%                                                                             %
X%   E m i t C o d e                                                           %
X%                                                                             %
X%                                                                             %
X%                                                                             %
X%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
X%
X%
X*/
Xstatic void EmitCode(opcode)
Xchar
X  opcode;
X{
X  if (code != &start_code)
X    *code++=opcode;
X  else
X    code_size++;
X}
X
X/*
X%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
X%                                                                             %
X%                                                                             %
X%                                                                             %
X%   I n s e r t                                                               %
X%                                                                             %
X%                                                                             %
X%                                                                             %
X%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
X%
X%  Function Insert inserts an operator in front of an already-emitted operand.
X%
X*/
Xstatic void Insert(opcode,operand)
Xchar
X  opcode,
X  *operand;
X{
X  register char
X    *p,
X    *place,
X    *q;
X
X  if (code == &start_code)
X    {
X      code_size+=3;
X      return;
X    }
X  p=code;
X  code+=3;
X  q=code;
X  while (p > operand)
X    *--q=(*--p);
X  place=operand;
X  *place++=opcode;
X  *place++='\0';
X  *place++='\0';
X}
X
X/*
X%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
X%                                                                             %
X%                                                                             %
X%                                                                             %
X%   M a t c h                                                                 %
X%                                                                             %
X%                                                                             %
X%                                                                             %
X%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
X%
X%
X*/
Xstatic int Match(regular_expression)
Xchar
X  *regular_expression;
X{
X  char
X    *next_token,
X    *NextToken();
X
X  register char
X    *scan;
X
X  scan=regular_expression;
X  while (scan != NULL)
X  {
X    next_token=NextToken(scan);
X    switch(OpCode(scan))
X    {
X      case MatchBeginningOfLine:
X      {
X        if (p != start_pattern)
X          return(0);
X        break;
X      }
X      case MatchEndOfProgramOfLine:
X      {
X        if (*p != '\0')
X         return(0);
X        break;
X      }
X      case MatchAnyCharacter:
X      {
X        if (*p == '\0')
X          return(0);
X        p++;
X        break;
X      }
X      case MatchExactly:
X      {
X        register char
X          *operand;
X
X        register int
X          length;
X
X        operand=Operand(scan);
X        /*
X          Inline the first character for speed.
X        */
X        if (*operand != *p)
X          return(0);
X        length=strlen(operand);
X        if ((length > 1) && (strncmp(operand,p,length) != 0))
X          return(0);
X        p+=length;
X        break;
X      }
X      case MatchAnyCharacterOf:
X      {
X        if ((*p == '\0' || strchr(Operand(scan),*p) == NULL))
X          return(0);
X        p++;
X        break;
X      }
X      case MatchAnyCharacterBut:
X      {
X        if ((*p == '\0') || (strchr(Operand(scan),*p) != NULL))
X          return(0);
X        p++;
X        break;
X      }
X      case MatchEmptyString:
X        break;
X      case Back:
X        break;
X      case Open+1:
X      case Open+2:
X      case Open+3:
X      case Open+4:
X      case Open+5:
X      case Open+6:
X      case Open+7:
X      case Open+8:
X      case Open+9:
X      {
X        register char
X          *save;
X
X        register int
X          no;
X
X        no=OpCode(scan)-Open;
X        save=p;
X        if (!Match(next_token))
X          return(0);
X        else
X          {
X            /*
X              Don't set subpattern if some later invocation of the same
X              parentheses already has.
X            */
X            if (subpattern[no] == NULL)
X              subpattern[no]=save;
X            return(1);
X          }
X        break;
X      }
X      case Close+1:
X      case Close+2:
X      case Close+3:
X      case Close+4:
X      case Close+5:
X      case Close+6:
X      case Close+7:
X      case Close+8:
X      case Close+9:
X      {
X        register char
X          *save;
X
X        register int
X          no;
X
X        no=OpCode(scan)-Close;
X        save=p;
X        if (!Match(next_token))
X           return(0);
X        else
X          {
X            /*
X              Don't set subpattern_end if some later invocation of the same 
X              parentheses already has.
X            */
X            if (subpattern_end[no] == NULL)
X              subpattern_end[no]=save;
X            return(1);
X          }
X        break;
X      }
X      case MatchThisOrNext:
X      {
X        register char
X          *save;
X
X        if (OpCode(next_token) != MatchThisOrNext)
X          next_token=Operand(scan);  
X        else
X          {
X            do
X            {
X              save=p;
X              if (Match(Operand(scan)))
X                return(1);
X              p=save;
X              scan=NextToken(scan);
X            } while ((scan != NULL) && (OpCode(scan) == MatchThisOrNext));
X            return(0);
X          }
X        break;
X      }
X      case MatchZeroOrMore:
X      case MatchOneOrMore:
X      {
X        register char
X          next_tokench,
X          *save;
X
X        register int
X          min,
X          no;
X
X        /*
X          Lookahead to avoid useless match attempts when we know what
X          character comes next_token.
X        */
X        next_tokench='\0';
X        if (OpCode(next_token) == MatchExactly)
X          next_tokench=(*Operand(next_token));
X        min=(OpCode(scan) == MatchZeroOrMore) ? 0 : 1;
X        save=p;
X        no=Repeat(Operand(scan));
X        while (no >= min)
X        {
X          /*
X            If it could work, try it.
X          */
X          if ((next_tokench == '\0') || (*p == next_tokench))
X            if (Match(next_token))
X              return(1);
X          /*
X            Couldn't or didn't -- back up.
X          */
X          no--;
X          p=save+no;
X        }
X        return(0);
X        break;
X      }
X      case EndOfProgram:
X        return(1);
X        break;
X      default:
X        (void) fprintf(stderr,"Regular(3): %s","memory corruption");
X        return(0);
X        break;
X    }
X    scan=next_token;
X  }
X  (void) fprintf(stderr,"Regular(3): %s","corrupted pointers");
X  return(0);
X}
X
X/*
X%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
X%                                                                             %
X%                                                                             %
X%                                                                             %
X%   N e x t T o k e n                                                         %
X%                                                                             %
X%                                                                             %
X%                                                                             %
X%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
X%
X%
X*/
Xstatic char *NextToken(p)
Xregister char
X  *p;
X{
X  register int
X    offset;
X
X  if (p == &start_code)
X    return(NULL);
X  offset=Next(p);
X  if (offset == 0)
X    return(NULL);
X  if (OpCode(p) == Back)
X    return(p-offset);
X  else
X    return(p+offset);
X}
X
X/*
X%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
X%                                                                             %
X%                                                                             %
X%                                                                             %
X%   N o d e                                                                   %
X%                                                                             %
X%                                                                             %
X%                                                                             %
X%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
X%
X%
X*/
Xstatic char *Node(opcode)
Xchar
X  opcode;
X{
X  register char
X    *ptr,
X    *status;
X
X  status=code;
X  if (status == &start_code)
X    {
X      code_size+=3;
X      return(status);
X    }
X  ptr=status;
X  *ptr++=opcode;
X  *ptr++='\0';
X  *ptr++='\0';
X  code=ptr;
X  return(status);
X}
X
X/*
X%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
X%                                                                             %
X%                                                                             %
X%                                                                             %
X%   O p T a i l                                                               %
X%                                                                             %
X%                                                                             %
X%                                                                             %
X%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
X%
X%
X*/
Xstatic void OpTail(p,value)
Xchar
X  *p;
X
Xchar
X  *value;
X{
X  void
X    Tail();
X
X  /*
X    "Operandless" and "op != MatchThisOrNext" are synonymous in practice.
X  */
X  if ((p == NULL) || (p == &start_code) || (OpCode(p) != MatchThisOrNext))
X    return;
X  Tail(Operand(p),value);
X}
X
X/*
X%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
X%                                                                             %
X%                                                                             %
X%                                                                             %
X%   P i e c e                                                                 %
X%                                                                             %
X%                                                                             %
X%                                                                             %
X%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
X%
X%
X*/
Xstatic char *Piece(flagp)
Xint
X  *flagp;
X{
X  int
X    flags;
X
X  register char
X    *next_token,
X    op,
X    *status;
X
X  void
X    Tail();
X
X  status=Atom(&flags);
X  if (status == NULL)
X    return(NULL);
X  op=(*token);
X  if (!MultipleMatches(op))
X    {
X      *flagp=flags;
X      return(status);
X    }
X  if (!(flags & NonNull) && op != '?')
X    Fail("*+ operand could be empty");
X  *flagp=(op != '+') ? (WorstCase | SpecialStart) : (WorstCase | NonNull);
X  if (op == '*' && (flags & Simple))
X    Insert(MatchZeroOrMore,status);
X  else
X    if (op == '*')
X      {
X        /*
X          Emit x* as (x&|), where & means "self".
X        */
X        Insert(MatchThisOrNext,status);  
X        OpTail(status,Node(Back)); 
X        OpTail(status,status); 
X        Tail(status,Node(MatchThisOrNext));  
X        Tail(status,Node(MatchEmptyString));
X      }
X    else
X      if ((op == '+') && (flags & Simple))
X        Insert(MatchOneOrMore,status);
X      else
X        if (op == '+')
X          {
X            /*
X              Emit x+ as x (&|), where & means "self".
X            */
X            next_token=Node(MatchThisOrNext);  
X            Tail(status,next_token);
X            Tail(Node(Back),status);
X            Tail(next_token,Node(MatchThisOrNext));
X            Tail(status,Node(MatchEmptyString));
X          }
X        else
X          if (op == '?')
X            {
X              /*
X                Emit x? as (x|)
X              */
X              Insert(MatchThisOrNext,status);  
X              Tail(status,Node(MatchThisOrNext));  
X              next_token=Node(MatchEmptyString);  
X              Tail(status,next_token);
X              OpTail(status,next_token);
X            }
X  token++;
X  if (MultipleMatches(*token))
X    Fail("nested *?+");
X  return(status);
X}
X
X/*
X%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
X%                                                                             %
X%                                                                             %
X%                                                                             %
X%   R e g u l a r                                                             %
X%                                                                             %
X%                                                                             %
X%                                                                             %
X%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
X%
X%
X*/
Xstatic char *Regular(parenthesized,flagp)
Xint
X  parenthesized;
X
Xint
X  *flagp;
X{
X  int
X    flags;
X
X  register char
X    *br,
X    *ender,
X    *status;
X
X  register int
X    count;
X
X  void
X    Tail();
X
X  count=0;
X  *flagp=NonNull;
X  if (!parenthesized)
X    status=NULL;
X  else
X    {
X      /*
X        Make an Open node.
X      */
X      if (number_parenthesis >= NumberSubExpressions)
X        Fail("too many ()");
X      count=number_parenthesis;
X      number_parenthesis++;
X      status=Node(Open+count);
X    }
X  /*
X    Pick up the branches, linking them together.
X  */
X  br=Branch(&flags);
X  if (br == NULL)
X    return(NULL);
X  if (status != NULL)
X    Tail(status,br);
X  else
X    status=br;
X  if (!(flags & NonNull))
X    *flagp&=(~NonNull);
X  *flagp|=flags & SpecialStart;
X  while (*token == '|')
X  {
X    token++;
X    br=Branch(&flags);
X    if (br == NULL)
X      return(NULL);
X    Tail(status,br);
X    if (!(flags & NonNull))
X      *flagp &= ~NonNull;
X    *flagp|=flags & SpecialStart;
X  }
X  /*
X    Make a closing node and hook it on the end.
X  */
X  ender=Node((parenthesized) ? Close+count : EndOfProgram);
X  Tail(status,ender);
X  /*
X    Hook the tails of the branches to the closing node.
X  */
X  for(br=status; br != NULL; br=NextToken(br))
X    OpTail(br,ender);
X  /*
X    Check for proper termination.
X  */
X  if (parenthesized && (*token++ != ')'))
X    Fail("unmatched()")
X  else
X    if (!parenthesized && (*token != '\0'))
X      {
X        if (*token == ')')
X          Fail("unmatched()")
X        else
X          Fail("junk on end")
X       }
X  return(status);
X}
X
X/*
X%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
X%                                                                             %
X%                                                                             %
X%                                                                             %
X%   R e p e a t                                                               %
X%                                                                             %
X%                                                                             %
X%                                                                             %
X%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
X%
X%
X*/
Xstatic int Repeat(p)
Xchar
X  *p;
X{
X  register char
X    *operand,
X    *scan;
X
X  register int
X    count=0;
X
X  scan=p;
X  operand=Operand(p);
X  switch(OpCode(p))
X  {
X    case MatchAnyCharacter:
X    {
X      count=strlen(scan);
X      scan+=count;
X      break;
X    }
X    case MatchExactly:
X    {
X      while (*operand == *scan)
X      {
X        count++;
X        scan++;
X      }
X      break;
X    }
X    case MatchAnyCharacterOf:
X    {
X      while ((*scan != '\0') && (strchr(operand,*scan) != NULL))
X      {
X        count++;
X        scan++;
X      }
X      break;
X    }
X    case MatchAnyCharacterBut:
X    {
X      while ((*scan != '\0') && (strchr(operand,*scan) == NULL))
X      {
X        count++;
X        scan++;
X      }
X      break;
X    }
X    default:
X    {
X      (void) fprintf(stderr,"Regular(3): %s","internal foulup");
X      count=0;
X      break;
X    }
X  }
X  p=scan;
X  return(count);
X}
X
X/*
X%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
X%                                                                             %
X%                                                                             %
X%                                                                             %
X%   T a i l                                                                   %
X%                                                                             %
X%                                                                             %
X%                                                                             %
X%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
X%
X%
X*/
Xstatic void Tail(p,val)
Xchar
X  *p;
X
Xchar
X  *val;
X{
X  register char
X    *scan,
X    *temp;
X
X  register int
X    offset;
X
X  if (p == &start_code)
X    return;
X  /*
X    Find last node.
X  */
X  scan=p;
X  for(;;)
X  {
X    temp=NextToken(scan);
X    if (temp == NULL)
X      break;
X    scan=temp;
X  }
X  if (OpCode(scan) == Back)
X    offset=scan-val;
X  else
X    offset=val-scan;
X  *(scan+1)=(offset >> 8) & 0377;
X  *(scan+2)=offset & 0377;
X}
X
X/*
X%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
X%                                                                             %
X%                                                                             %
X%                                                                             %
X%   T r y                                                                     %
X%                                                                             %
X%                                                                             %
X%                                                                             %
X%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
X%
X%
X*/
Xstatic int Try(regular_expression,pattern)
XRegularExpression
X  *regular_expression;
X
Xchar
X  *pattern;
X{
X  register char
X    **ep,
X    **sp;
X
X  register int
X    i;
X
X  p=pattern;
X  subpattern=regular_expression->subpattern;
X  subpattern_end=regular_expression->subpattern_end;
X  sp=regular_expression->subpattern;
X  ep=regular_expression->subpattern_end;
X  for(i=NumberSubExpressions; i > 0; i--)
X  {
X    *sp++=NULL;
X    *ep++=NULL;
X  }
X  if (!Match(regular_expression->program+1))
X    return(0);
X  else
X    {
X      regular_expression->subpattern[0]=pattern;
X      regular_expression->subpattern_end[0]=p;
X      return(1);
X    }
X}
X
X/*
X%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
X%                                                                             %
X%                                                                             %
X%                                                                             %
X%   C o m p i l e R e g u l a r E x p r e s s i o n                           %
X%                                                                             %
X%                                                                             %
X%                                                                             %
X%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
X%
X%  Function CompileRegularExpression compiles a regular expression into a 
X%  structure of type RegularExpression and returns a pointer to it.  The space 
X%  is allocated using function malloc and may be released by function free.
X%
X%
X*/
XRegularExpression *CompileRegularExpression(regular_expression)
Xchar
X  *regular_expression;
X{
X  int
X    flags;
X
X  register char
X    *longest,
X    *scan;
X
X  register RegularExpression
X    *r;
X
X  register int
X    length;
X
X  if (regular_expression == NULL)
X    Fail("NULL argument");
X  /*
X    First pass: determine size.
X  */
X  token=regular_expression;
X  number_parenthesis=1;
X  code_size=0L;
X  code=(&start_code);
X  EmitCode(Magick);
X  if (Regular(0,&flags) == NULL)
X    return(NULL);
X  /*
X    Allocate space.
X  */
X  r=(RegularExpression *)
X    malloc((unsigned int) (code_size+sizeof(RegularExpression)));
X  if (r == NULL)
X    Fail("out of space");
X  /*
X    Second pass: emit code.
X  */
X  token=regular_expression;
X  number_parenthesis=1;
X  code=r->program;
X  EmitCode(Magick);
X  if (Regular(0,&flags) == NULL)
X    return(NULL);
X  /*
X    Dig out information for optimizations.
X  */
X  r->start_character='\0';
X  r->anchor=0;
X  r->priority_pattern=NULL;
X  r->pattern_length=0;
X  scan=r->program+1;
X  if (OpCode(NextToken(scan)) == EndOfProgram)
X    {
X      scan=Operand(scan);
X      if (OpCode(scan) == MatchExactly)
X        r->start_character=(*Operand(scan));
X      else
X        if (OpCode(scan) == MatchBeginningOfLine)
X          r->anchor++;
X      /*
X        If there's something expensive in the regular expression, find the
X        longest literal pattern that must appear and make it the 
X        priority_pattern.
X      */
X      if (flags & SpecialStart)
X        {
X          longest=NULL;
X          length=0;
X          for(; scan != NULL; scan=NextToken(scan))
X            if ((OpCode(scan) == MatchExactly) && 
X                (strlen(Operand(scan)) >= length))
X              {
X                longest=Operand(scan);
X                length=strlen(Operand(scan));
X              }
X          r->priority_pattern=longest;
X          r->pattern_length=length;
X        }
X    }
X  return(r);
X}
X
X/*
X%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
X%                                                                             %
X%                                                                             %
X%                                                                             %
X%   E x e c u t e R e g u l a r E x p r e s s i o n                           %
X%                                                                             %
X%                                                                             %
X%                                                                             %
X%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%%
X%
X%  Function ExecuteRegularExpression matches a NULL-terminated pattern against 
X%  the compiled regular expression in regular-expression.  It returns 1 for 
X%  success and 0 for failure.
X%
X%
X*/
Xint ExecuteRegularExpression(regular_expression,pattern)
Xregister RegularExpression
X  *regular_expression;
X
Xregister char
X  *pattern;
X{
X  register char
X    *s;
X
X  if ((regular_expression == (RegularExpression *) NULL) || 
X      (pattern == (char *) NULL))
X    {
X      (void) fprintf(stderr,"Regular(3): %s","NULL parameter\n");
X      return(0);
X    }
X  /*
X    Check validity of program.
X  */
X  if (((int)*(unsigned char *)(regular_expression->program)) != Magick)
X    {
X      (void) fprintf(stderr,"Regular(3): %s","corrupted program");
X      return(0);
X    }
X  /*
X    If there is a "must appear" pattern, look for it.
X  */
X  if (regular_expression->priority_pattern != NULL)
X    {
X      s=pattern;
X      while ((s=strchr(s,regular_expression->priority_pattern[0])) != NULL)
X      {
X        if (strncmp(s,regular_expression->priority_pattern,
X            regular_expression->pattern_length) == 0)
X          break;
X        s++;
X       }
X       if (s == NULL)
X         return(0);
X    }
X  /*
X    Mark beginning of line for ^.
X  */
X  start_pattern=pattern;
X  /*
X    Simplest case:  anchored match need be tried only once.
X  */
X  if (regular_expression->anchor)
X    return(Try(regular_expression,pattern));
X  /*
X    Messy cases:  unanchored match.
X  */
X  s=pattern;
X  if (regular_expression->start_character != '\0')
X    while ((s=strchr(s,regular_expression->start_character)) != NULL)
X    {
X      if (Try(regular_expression,s))
X        return(1);
X      s++;
X    }
X  else
X    do
X    {
X      if (Try(regular_expression,s))
X        return(1);
X    } while (*s++ != '\0');
X  return(0);
X}
END_OF_FILE
if test 33286 -ne `wc -c <'regular.c'`; then
    echo shar: \"'regular.c'\" unpacked with wrong size!
fi
chmod +x 'regular.c'
# end of 'regular.c'
fi
echo shar: End of shell archive.
exit 0
--
The UUCP Mailer