Article 2722 of comp.sources.unix:
Path: nntpd.lkg.dec.com!crl.dec.com!crl.dec.com!caen!msunews!agate!howland.reston.ans.net!newsfeed.internetmci.com!in1.uu.net!vixie!vixie!not-for-mail
From: orel@dxunk1.cern.ch (Oleg Orel)
Newsgroups: comp.sources.unix
Subject: v29i065: libftp - C library for FTP client access, V5.0, Part04/08
Date: 12 Dec 1995 00:35:56 -0800
Organization: Vixie Enterprises
Lines: 1947
Sender: vixie@vix.com
Approved: vixie@gw.home.vix.com
Message-ID: <4ajetc$amg@gw.home.vix.com>
References: <1.818757291.10931@gw.home.vix.com>
NNTP-Posting-Host: gw.home.vix.com

Submitted-By: orel@dxunk1.cern.ch (Oleg Orel)
Posting-Number: Volume 29, Issue 65
Archive-Name: libftp-5.0/part04

#!/bin/sh
# to extract, remove the header and type "sh filename"
if `test ! -d ./utils`
then
  mkdir ./utils
  echo "mkdir ./utils"
fi
if `test ! -s ./utils/glob.h`
then
echo "writing ./utils/glob.h"
cat > ./utils/glob.h << '\End\Of\Shar\'
/*
 * Copyright (c) 1989, 1993
 *	The Regents of the University of California.  All rights reserved.
 *
 * This code is derived from software contributed to Berkeley by
 * Guido van Rossum.
 *
 * Redistribution and use in source and binary forms, with or without
 * modification, are permitted provided that the following conditions
 * are met:
 * 1. Redistributions of source code must retain the above copyright
 *    notice, this list of conditions and the following disclaimer.
 * 2. Redistributions in binary form must reproduce the above copyright
 *    notice, this list of conditions and the following disclaimer in the
 *    documentation and/or other materials provided with the distribution.
 * 3. All advertising materials mentioning features or use of this software
 *    must display the following acknowledgement:
 *	This product includes software developed by the University of
 *	California, Berkeley and its contributors.
 * 4. Neither the name of the University nor the names of its contributors
 *    may be used to endorse or promote products derived from this software
 *    without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND
 * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
 * ARE DISCLAIMED.  IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE
 * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
 * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
 * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
 * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
 * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE.
 *
 *	@(#)glob.h	8.1 (Berkeley) 6/2/93
 */

#ifndef _GLOB_H_
#define	_GLOB_H_


#include <cdefs.h>

struct stat;
typedef struct {
	int gl_pathc;		/* Count of total paths so far. */
	int gl_matchc;		/* Count of paths matching pattern. */
	int gl_offs;		/* Reserved at beginning of gl_pathv. */
	int gl_flags;		/* Copy of flags parameter to glob. */
	char **gl_pathv;	/* List of paths matching pattern. */
				/* Copy of errfunc parameter to glob. */
	int (*gl_errfunc) __P((const char *, int));

	/*
	 * Alternate filesystem access methods for glob; replacement
	 * versions of closedir(3), readdir(3), opendir(3), stat(2)
	 * and lstat(2).
	 */
	void (*gl_closedir) __P((void *));
	struct dirent *(*gl_readdir) __P((void *));	
	void *(*gl_opendir) __P((const char *));
	int (*gl_lstat) __P((const char *, struct stat *));
	int (*gl_stat) __P((const char *, struct stat *));
} glob_t;

#define	GLOB_APPEND	0x0001	/* Append to output from previous call. */
#define	GLOB_DOOFFS	0x0002	/* Use gl_offs. */
#define	GLOB_ERR	0x0004	/* Return on error. */
#define	GLOB_MARK	0x0008	/* Append / to matching directories. */
#define	GLOB_NOCHECK	0x0010	/* Return pattern itself if nothing matches. */
#define	GLOB_NOSORT	0x0020	/* Don't sort. */

#define	GLOB_ALTDIRFUNC	0x0040	/* Use alternately specified directory funcs. */
#define	GLOB_BRACE	0x0080	/* Expand braces ala csh. */
#define	GLOB_MAGCHAR	0x0100	/* Pattern had globbing characters. */
#define	GLOB_NOMAGIC	0x0200	/* GLOB_NOCHECK without magic chars (csh). */
#define	GLOB_QUOTE	0x0400	/* Quote special chars with \. */
#define	GLOB_TILDE	0x0800	/* Expand tilde names from the passwd file. */

#define	GLOB_NOSPACE	(-1)	/* Malloc call failed. */
#define	GLOB_ABEND	(-2)	/* Unignored error. */

__BEGIN_DECLS
int	glob __P((const char *, int, int (*)(const char *, int), glob_t *));
void	globfree __P((glob_t *));
__END_DECLS

#endif /* !_GLOB_H_ */
\End\Of\Shar\
else
  echo "will not over write ./utils/glob.h"
fi
if `test ! -s ./utils/list.c`
then
echo "writing ./utils/list.c"
cat > ./utils/list.c << '\End\Of\Shar\'
static char rcsid[] = "$Id: list.c,v 5.0 1995/12/10 10:34:21 orel Exp $";

/*
  $Log: list.c,v $
 * Revision 5.0  1995/12/10  10:34:21  orel
 * LIBFTP Version 5.0 (Distributed revision)
 *
 * Revision 4.2  1995/09/09  09:51:49  orel
 *  Change type String to FtpString for clean conflicting with
 *  X11's type string which is pointer to character only.
 *  Thanks for MIT for this good name of type.
 *
 * Revision 4.2  1995/09/09  09:51:49  orel
 *  Change type String to FtpString for clean conflicting with
 *  X11's type string which is pointer to character only.
 *  Thanks for MIT for this good name of type.
 *
 * Revision 4.1  1995/08/19  18:42:25  orel
 * *** empty log message ***
 *
 * Revision 4.1  1995/08/19  18:42:25  orel
 * *** empty log message ***
 *
 * Revision 4.0  1995/07/11  14:51:08  orel
 * Libftp version 4.0
 *
 * Revision 4.0  1995/07/11  14:51:08  orel
 * Libftp version 4.0
 *
 * Revision 3.1  1995/06/21  09:28:34  orel
 * Porting to AIX and some modification..........
 *
 * Revision 3.1  1995/06/21  09:28:34  orel
 * Porting to AIX and some modification..........
 *
 * Revision 3.0  1995/03/20  05:26:29  orel
 * *** empty log message ***
 *
 * Revision 3.0  1995/03/20  05:26:29  orel
 * *** empty log message ***
 *
 * Revision 1.3  1995/03/20  05:17:54  orel
 * *** empty log message ***
 *
 * Revision 1.2  1995/03/20  05:17:31  orel
 * *** empty log message ***
 *
 * Revision 1.2  1995/03/20  05:17:31  orel
 * *** empty log message ***
 *
 * Revision 1.1  1995/03/16  09:38:04  orel
 * Initial revision
 *
*/

#include "list.h"
#include "uftp.h"
#include <stdio.h>


list_init(LIST **list)
{
  if (*list==NULL) return;
  
  if ((*list)->next!=NULL)
    list_init(&((*list)->next));

  free(*list);
  *list=NULL;
}

list_add(LIST **list,char *item)
{
  register LIST *t;

  for (t=(*list);;t=t->next)
    {
      if (t==NULL)
	{
	  t=(LIST *) malloc(sizeof(LIST));
	  if (t==NULL) return -1;
	  bzero(t,sizeof *t);
	  break;
	}
      
      if (!strcmp(t->item,item)) return 0; /* ALready in list */

      if (t->next==NULL)
	{
	  t = t->next = (LIST *) malloc(sizeof(LIST));
	  if (t==NULL) return -1;
	  bzero(t,sizeof *t);
	  break;
	}
    }

  strcpy(t->item,item);
  t->next=NULL;
  if ((*list)==NULL) (*list)=t;
  
  return 1;
}

list_count(LIST **list)
{
  register int count=0;
  register LIST *t;
  
  for(t=(*list);t!=NULL;t=t->next)
    count++;

  return count;
}

list_remove(LIST **list, char *item)
{
    register LIST *t,*prev;

    for(t=(*list);t!=NULL;prev=t,t=t->next)
      {
	if (!strcmp(t->item,item))
	  {
	    if ((*list)=t)
	      {
		*list=t->next;
		free(t);
		return;
	      }

	    prev -> next = t -> next;

	    free(t);
	    return;
	  }
      }
    
}



list_sort(LIST **list)
{
  LIST *l,*ll;
  FtpString tmp;

  for (l=(*list);l!=NULL && (l->next)!=NULL ;l=l->next)
    for (ll=l;ll!=NULL;ll=ll->next)
      if (strcmp(l->item,ll->item)>0)
	{
	  strcpy(tmp,l->item);
	  strcpy(l->item,ll->item);
	  strcpy(ll->item,tmp);
	}
}
\End\Of\Shar\
else
  echo "will not over write ./utils/list.c"
fi
if `test ! -s ./utils/list.h`
then
echo "writing ./utils/list.h"
cat > ./utils/list.h << '\End\Of\Shar\'
#ifndef __LIST_H__
#define __LIST_H__
typedef struct _list
{
  char item[1024];
  struct _list *next;
} LIST;


int list_init(LIST**);
int list_add(LIST**,char*);
int list_remove(LIST**,char*);
int list_count(LIST**);
#endif


\End\Of\Shar\
else
  echo "will not over write ./utils/list.h"
fi
if `test ! -s ./utils/uftp.c`
then
echo "writing ./utils/uftp.c"
cat > ./utils/uftp.c << '\End\Of\Shar\'
static char rcsid[] = "$Id: uftp.c,v 5.0 1995/12/10 10:34:21 orel Exp $";

/*
	$Log: uftp.c,v $
 * Revision 5.0  1995/12/10  10:34:21  orel
 * LIBFTP Version 5.0 (Distributed revision)
 *
 * Revision 4.6  1995/12/02  11:24:01  orel
 * pathes for proxy get thru www-daemon
 *
 * Revision 4.5  1995/11/09  19:19:04  orel
 * Clean problem with end of last command at the end of current command
 * if last one shortly then first
 * ,
 *
 * Revision 4.5  1995/11/09  19:19:04  orel
 * Clean problem with end of last command at the end of current command
 * if last one shortly then first
 * ,
 *
 * Revision 4.4  1995/09/09  09:51:49  orel
 *  Change type String to FtpString for clean conflicting with
 *  X11's type string which is pointer to character only.
 *  Thanks for MIT for this good name of type.
 *
 * Revision 4.3  1995/09/09  07:48:33  orel
 * Some corrections for trafic log file
 *
 * Revision 4.2  1995/08/19  18:42:25  orel
 * *** empty log message ***
 *
 * Revision 4.1  1995/08/19  18:07:06  orel
 * Fitch for calculate trafic for log
 *
 * Revision 4.0  1995/07/11  14:51:08  orel
 * Libftp version 4.0
 *
 * Revision 4.0  1995/07/11  14:51:08  orel
 * Libftp version 4.0
 *
 * Revision 3.5  1995/07/04  13:37:12  orel
 * chenge compctl function
 *
 * Revision 3.4  1995/06/28  13:52:37  orel
 * *** empty log message ***
 *
 * Revision 3.3  1995/06/28  13:37:26  orel
 * *** empty log message ***
 *
 * Revision 3.2  1995/06/21  09:28:34  orel
 * Porting to AIX and some modification..........
 *
 * Revision 3.1  1995/06/20  15:54:28  orel
 * Porting to AIX
 *
 * Revision 3.0  1995/03/20  05:26:29  orel
 * *** empty log message ***
 *
 * Revision 2.14  1995/03/20  05:17:54  orel
 * *** empty log message ***
 *
 * Revision 2.13  1995/03/20  05:17:31  orel
 * *** empty log message ***
 *
 * Revision 2.13  1995/03/20  05:17:31  orel
 * *** empty log message ***
 *
 * Revision 2.12  1995/03/16  09:38:04  orel
 * SIGPIPE, hosts, ........
 *
 * Revision 2.11  1995/03/07  16:01:40  orel
 * *** empty log message ***
 *
 * Revision 2.10  1995/03/05  15:02:08  orel
 * *** empty log message ***
 *
 * Revision 2.9  1995/02/26  16:41:21  orel
 * add recursive rm and recursive put
 *
 * Revision 2.8  1995/02/26  12:44:02  orel
 * *** empty log message ***
 *
 * Revision 2.7  1995/02/18  15:42:27  orel
 * add recursive mget
 *
 * Revision 2.6  1995/02/17  12:47:14  orel
 * add parallel mget
 *
 * Revision 2.6  1995/02/17  12:47:14  orel
 * add parallel mget
 *
 * Revision 2.5  1995/02/13  16:05:45  orel
 * add setargs
 *
 * Revision 2.4  1995/02/04  09:12:51  orel
 * rcs id
 *

*/
/* File Transfer Protocol Toolkit based on libftp */

#include "uftp.h"
#include <varargs.h>


FTP *ftp[NFRAMES];
LINKINFO iftp[NFRAMES];
int frame=0;


int status;
jmp_buf start;
int lastcmd=0;
int trymode=1;
int restmode=1;
int hashmode=0;
int sleeptime=30;
int winsize;
int interactive=0;

time_t noopinterval=0;
time_t nooptimeout=1;
time_t prevtime=0;

FtpString prompt="%T %u@%H:%d> ";
FtpString defaultuser;

FtpString www_gateway="www.cern.ch";
int www_port=8080;

ALIAS *firstalias=NULL;
LIST *hosts=NULL;


/* if main have any arguments, interprets each it as command with args */


main(int argc, char **argv)
{
  register int i;
  register char *p1;

  FtpString params;
  FtpString tmp1,tmp2;
  
  if (setjmp(start)!=0) 
    goto main_loop;
  
  setsignals();

#if defined(TIOCGWINSZ) && defined(SIGWINCH)
  getwinsz();
#endif
  
  FtpSetErrorHandler(&FtpInit,my_error);
  FtpSetIOHandler(&FtpInit,my_error);
  
  strcpy(defaultuser,getpwuid(getuid())->pw_name);

  
  memset(ftp,0,sizeof(FTP*)*NFRAMES);
  memset(iftp,0,sizeof(LINKINFO)*NFRAMES);
  
  fprintf(stderr,"Welcome to uftp version %s (%s)\n",
	  strcpy(tmp1,word(rcsid,3)),
	  strcpy(tmp2,word(rcsid,4)));
  
  batch(SYSTEMRC);

  for (i=1, params[0]=0; i< argc; i++)
    {
      strcat(params,argv[i]);
      if (i+1!=argc) strcat(params," ");
    }
  
  load_link("ftp","open");
  load_link("ftp","connect");
  load_link("ftp","preconnect");
  
  
  batch(getrcname());
  batch(getaliasrcname());
  batch(getsetsrcname());

  load_compctl();

  if (params[0]!=0)
    {
      FtpString new;

      strcpy(new,"open ");
      
      if (ifalias(params)||ifcmd(params)) 
	  execute (params);
      else
	strcat(new,params),
	execute(new);
    }


  gl_tab_hook=/*tab*/compctl_hook;
  
 main_loop:

  interactive=1;
  
  setsignals();
  
  while (1)
    {
      
      setjmp(start);
      if (lastcmd) quit(0);
      
      
      p1=getline(getprompt());

      if (p1==NULL||*p1==0) 
	{
	  quit(0);
	}
      
      if (strchr(p1,'\n')!=NULL) *(char*)strchr(p1,'\n')=0;

      if (*word(p1,1)==0) continue;
      
      gl_histadd(p1);
      execute(p1);
    }
}
/* Exacute few command separated by ';' . The character ' must use for mark complex 
   works*/  

execute (char *cmd)
{
  FtpString w1,w2,w3,w4,w5,w6;
  FtpString newcmd;
  char *p;

  
  if (!*cmd || *cmd=='#' ) return;

  for ( p=newcmd ; *cmd; cmd++)
    {
      if ( *cmd == '\'' )
	{
	  *p++ = *cmd++;
	  while ( *cmd != '\'' && *cmd != 0 ) *p++ = *cmd++;
	  if ( *cmd == 0 ) 
	    return puts("Unbalanced \', please corrected!\n");
	  *p++ = *cmd;
	  continue;
	}
      
      if ( *cmd == ';' ) 
	{
	  *p=0;
	  execute(newcmd);
	  p=newcmd;
	  continue;
	}
      *p++ = *cmd;
    }

  
  *p=0;
  cmd=newcmd;
  
  if ( *cmd=='\\' ) 
    cmd++;
  else
    {
      FtpString new;
      strcpy(new,"\\");
      strcat(new,expandalias(cmd));
      return execute(new);
    }

  if ( *cmd == '!' ) 
    {
      int pid,_pid;
      int status;

      if (!(pid=fork()))
	{
	  execlp(((char*)getenv("SHELL")==NULL)?"/bin/sh":(char *)getenv("SHELL"),
		 "shell","-c",cmd+1,NULL);
	  exit(-1);
	}
      
      while(1)
	{
	  _pid=wait(&status);
	  if (_pid==pid)
	    return;
	}
    }
  
      
  redir(cmd);

  if (cmd[strlen(cmd)-1]=='&')
    {
      FtpString tmp;

      cmd[strlen(cmd)-1]=0;

      strcpy(tmp,"bg ");
      strcat(tmp,cmd);

      strcpy(cmd,tmp);
    }
  
  strcpy(w1,word(cmd,1));
  strcpy(w2,word(cmd,2));
  strcpy(w3,word(cmd,3));
  strcpy(w4,word(cmd,4));
  strcpy(w5,word(cmd,5));
  strcpy(w6,word(cmd,6));
  
  return executev(w1,w2,w3,w4,w5,w6);

  
}

executev(ARGS)
{
  CMDS *xcmd = &cmds[0];

  
  if (isdigit(*w1))
    return 
      atoi(w1)<NFRAMES?frame=atoi(w1):0,
      executev(w2,w3,w4,w5,w6,"");
  
  while ( xcmd -> cmd != NULL )
    {
      if ( !strcmp(xcmd->cmd,w1) && (xcmd -> func != NULL) )
	{
	  int status;
	  
	  if ( xcmd -> need && LINK == NULL)
	    return puts("Need connection to server");
	  iftp[frame].lock=1; unsetsignals();
	  status = (*xcmd->func)(w1,w2,w3,w4,w5,w6);
	  iftp[frame].lock=0; setsignals();
	  redirback();
	  return status;
	}
      xcmd++;
    }
  
  fprintf(stderr,"%s: unknown command\n",w1);
  return -1;
}


void intr(int sig)
{
  fprintf(stderr,"Interupted by signal %d\n",sig);
  if (LINK!=NULL) FtpSetHashHandler(LINK,NULL);
  setsignals();
  prevtime = time((time_t *)0);
  longjmp(start,1);
}

newframe(int connecteble)
{
  register int i;
  
  if (connecteble)
    for (i=0; i<NFRAMES; i++) if (ftp[i]!=NULL) return frame=i;
  for (i=0; i<NFRAMES; i++) if (ftp[i]==NULL) return frame=i;
  return -1;
}

STATUS my_error(FTP *ftp, int code, char *msg)
{

  if (code==LQUIT||(ftp==NULL)) log(msg);
  else
    FtpLog(ftp->title,msg);
  
  if ( abs(code) == 530 && (strstr(msg,"anonymous")!=NULL))
    {
      Ftp_reopen();
      longjmp(start,1);
    }
  longjmp(start,1);
}

char *getrcname()
{
  static FtpString rcpath;
  struct passwd *pwd=getpwuid(getuid());
  
  sprintf(rcpath,"%s/.uftprc",pwd->pw_dir);
  return rcpath;
}

char *getaliasrcname()
{
  static FtpString rcpath;
  struct passwd *pwd=getpwuid(getuid());
  
  sprintf(rcpath,"%s/.uftp_aliases",pwd->pw_dir);
  return rcpath;
}

char *getsetsrcname()
{
  static FtpString rcpath;
  struct passwd *pwd=getpwuid(getuid());
  
  sprintf(rcpath,"%s/.uftp_sets",pwd->pw_dir);
  return rcpath;
}

char *gethostsname()
{
  static FtpString rcpath;
  struct passwd *pwd=getpwuid(getuid());
  
  sprintf(rcpath,"%s/.uftp_hosts",pwd->pw_dir);
  return rcpath;
}



char *makestr(va_alist)
 va_dcl
{
  char *p1;
  va_list args;
  static FtpString new;

  new[0]=0;
  
  va_start(args);

  while(1)
    {
      p1=va_arg(args,char *);
      if (p1==NULL) break;
      if (*p1!=0) 
	{
	  if (new[0]!=0) strcat(new," ");
	  strcat(new,p1);
	}
    }
  va_end(args);
  return new;
}

  
#define ADD(str,chr) ( *(str)++ = (chr),*(str)=0)

INLINE ADDSTR(char **str, char *str1)
{
  while (*str1) *(*str)++ = *str1++;
  **str='\0';
}

char *expandalias(char *str)
{
  ALIAS *a=firstalias;
  static FtpString new;
  FtpString w1={0};
  char *p,*p1=new,*args;
  int dollar=0;

  new[0]=0;
      
  strcpy(w1,word(str,1));

  if ( (p=strchr(str,' '))!=NULL )
    args=p+1;
  else
    args="";
  
  while (a) 
    {
      if (!strcmp(a->name,w1))
	break;
      a=a->next;
    }
  
  if (!a) 
    return str;
  
  for ( p=a->str; *p; p++)
    {
      if ( *p != '$' ) 
	{
	  ADD(p1,*p);
	  continue;
	}
      
      dollar=1;
      p++;
      
      if (isdigit(*p)) 
	{
	  ADDSTR(&p1,word(str,(*p)-'0'+1));
	  continue;
	}

      switch (*p) 
	{ 
	  
	case '\0':
	case '$':  ADD(p1,'$');continue;
	case '*':  ADDSTR(&p1,args);continue;
	default:   ADD(p1,'$');ADD(p1,*p);continue;
	}
    }
  
  if (!dollar) 
    {
      ADD(p1,' ');
      ADDSTR(&p1,args);
    }

  *p=0;
  
  return new;
}

ifalias(char *cmd)
{
  if (*getalias(cmd)!=0)
    return 1;
  return 0;
}

char *getalias(char *cmd)
{
  FtpString what;
  ALIAS *a=firstalias;


  strcpy(what,word(cmd,1));
  
  while ( a!=NULL)
    {
      if (!strcmp(a->name,what))
	return a->str;
      a=a->next;
    }
  return "";
}


ifcmd(char *arg)
{
  CMDS *_cmds = &cmds[0];
  FtpString cmd;
  
  strcpy(cmd,word(arg,1));
  
  while (1)
    {
      if (_cmds->cmd==NULL) return 0;
      if (!strcmp(cmd,_cmds->cmd)) return 1;
      _cmds++;
    }
}


char *getprompt()
{
  
  static FtpString _prompt;
  FtpString tmp;
  char *s;

  _prompt[0]=0;

  for(s=prompt;*s;s++)
    switch (*s)
      {
      case '%':
	switch (*++s)
	  {
	  
	  case 'H': 
	    strcat(_prompt,iftp[frame].host);
	    break;
	    
	  case 'h':
	    strcpy(tmp,iftp[frame].host);
	    if (strchr(tmp,'.')!=NULL) *(char *)strchr(tmp,'.')=0;
	    strcat(_prompt,tmp);
	    break;

	  case 'M': 
	    gethostname(tmp, sizeof tmp);
	    strcat(_prompt,gethostbyname(tmp)->h_name);
	    break;
	    
	  case 'm':
	    gethostname(tmp, sizeof tmp);
	    strcpy(tmp,gethostbyname(tmp)->h_name);
	    if (strchr(tmp,'.')!=NULL) *(char *)strchr(tmp,'.')=0;
	    strcat(_prompt,tmp);
	    break;

	  case 'u':
	    strcat(_prompt,iftp[frame].user);
	    break;
	    
	  case 'd':
	    strcat(_prompt,iftp[frame].pwd);
	    break;

	  case 'D':
	    strcat(_prompt,(char *)getcwd(tmp,sizeof(tmp)));
	    break;

	  case 'f':
	    sprintf(tmp,"%d",frame);
	    strcat(_prompt,tmp);
	    break;

	  case 'p':
	    sprintf(tmp,"%d",(LINK==NULL)?0:LINK->port);
	    strcat(_prompt,tmp);
	    break;
	    
	  case 't':
	    
	    sprintf(tmp,"%d",(LINK==NULL)?0:LINK->timeout.tv_sec);
	    strcat(_prompt,tmp);
	    break;
	    

	  case 'T':
	    
	    {
	      time_t t=time((time_t *)0);
	      struct tm *lt=localtime(&t);
	      sprintf(tmp,"%02d:%02d:%02d",lt->tm_hour,
		      lt->tm_min,lt->tm_sec);
	      strcat(_prompt,tmp);
	    }
	    break;

	  case 'P':

	    sprintf(tmp,"%d",getpid());
	    strcat(_prompt,tmp);
	    break;
	    
	  default:
	    sprintf(tmp,"%%%c",*s);
	    strcat(_prompt,tmp);
	    break;
	  }
	break;

      case '^':
	
	++s;
	if (isalpha(*s))
	  {
	    sprintf(tmp,"%c",toupper(*s)-'A'+1);
	    strcat(_prompt,tmp);
	  }
	break;
	
      default:
	
	sprintf(tmp,"%c",*s);
	strcat(_prompt,tmp);
	break;
      }
  return _prompt;
}
	
	    
void noop()
{
  int i;
  time_t curtime,save;
  STATUS (*func1)(),(*func2)(),(*func3)();


  if (noopinterval==0) return;
  
  curtime = time((time_t *)0);
  
  signal(SIGALRM,noop);
  
  if (prevtime==0) 
    {
      prevtime=curtime;
      alarm(noopinterval);
      return;
    }
  
  if (curtime-prevtime < noopinterval) 
    {
      alarm(prevtime+noopinterval-curtime);
      return;
    }
 
  fprintf(stderr,"Waiting...");fflush(stderr);
 
  for (i=0;i<NFRAMES;i++)
    {
      if ( ftp[i]==NULL || FTPCMD(ftp[i]) == NULL || iftp[i].lock )
	continue;

      func1=ftp[i]->debug; ftp[i]->debug=NULL;
      func2=ftp[i]->error; ftp[i]->error=NULL;
      func3=ftp[i]->IO; ftp[i]->IO=NULL;
      save = ftp[i]->timeout.tv_sec;
      ftp[i]->timeout.tv_sec = nooptimeout;
      
      FtpCommand(ftp[i],"NOOP","",0,EOF);

      ftp[i]->timeout.tv_sec = save;
      ftp[i]->debug=func1;
      ftp[i]->error=func2;
      ftp[i]->IO=func3;

    }
  
  alarm(noopinterval);
  prevtime=curtime;
  
  for (i=0;i<10;i++) putc(8,stderr),putc(' ',stderr),putc(8,stderr);
  fflush(stderr);
}


setsignals()
{
  signal(SIGINT,intr);
  signal(SIGQUIT,intr);

#ifdef SIGURG
  signal(SIGURG,SIG_IGN);
#endif

  signal(SIGIO,SIG_IGN);
  signal(SIGCHLD,SIG_IGN);
  signal(SIGPIPE,SIG_IGN);

#if defined(TIOCGWINSZ) && defined(SIGWINCH)
  signal(SIGWINCH,getwinsz);
#endif

  noop();
}

unsetsignals()
{
  signal(SIGALRM,SIG_IGN);
  alarm(0);
}


int myhash(FTP *ftp,unsigned int chars)
{
  
  if (chars==0) return ftp -> counter=0;
  
  ftp -> counter += chars;
  
  if (hashmode)
    {
      fprintf(stderr,"%10u bytes transfered\r",ftp -> counter);
      fflush(stderr);
    }

  if (!lastcmd) 
    {
      noop();
      alarm(0);
    }
}



char *makefilename(char *f1, char *f2)
{
  char *p;
  
  if (*f2!=0)
    return f2;

  if ( (p=strrchr(f1,'/'))!=NULL)
    return p+1;
  return f1;
}

redir(char *cmdline)
{
  char *p=cmdline;
  FtpString result;
  char *r=result;
  
  for ( ; *p ; p++ , r++ )
    {
      if ( *p == '\\' ) 
	{
	  *r = * ++ p ;
	  continue;
	}
      
      if ( *p == '>' || *p == '<' )
	{
	  FtpString filename;
	  char *q=filename;
	  char c = *p;
	  
	  for (p++;isspace(*p)&&*p!=0;p++);
	  if (*p=='"')
	    {
	      for (p++; *p!='"' && *p!=0 ; p++,q++) *q = *p;
	      if (*p!='"') p++;
	    }
	  else
	    for (; !isspace(*p) && *p!=0 ; p++,q++) *q = *p;
	    
	  *q=0;
	  
	  if ( c == '>' ) 
	    output(filename);
	  else
	    input(filename);
	}
      *r = *p;
    }
  *r=0;
  strcpy(cmdline,result);
}

int itty = -1,otty = -1;
FILE *is=NULL, *os=NULL;


input(char *filename)
{
  
  if ((is=Ftpfopen(filename,"r"))==NULL)
    {
      perror(filename);
      return;
    }

  fflush(stdin);
  itty=dup(0);
  close(0);
  dup2(fileno(is),0);

}

output(char *filename)
{
  
  if ((os=Ftpfopen(filename,"w"))==NULL)
    {
      perror(filename);
      return;
    }
  
  fflush(stdout);
  otty=dup(1);
  close(1);
  dup2(fileno(os),1);
}

redirback()
{

  if (itty!= -1)
    {
      fflush(stdin);
      close(0);
      Ftpfclose(is);
      dup2(itty,0);
      is=NULL;
      itty = -1;
    }

  if (otty!= -1)
    {
      fflush(stdout);
      close(1);
      Ftpfclose(os);
      dup2(otty,1);
      os=NULL;
      otty = -1;
    }
}


batch(char *filename)
{
  FILE *fp;
  FtpString tmp;
  
  if ((fp=fopen(filename,"r"))!=NULL)
    {
      
      while ( fgets(tmp, sizeof tmp, fp) != NULL)
	{
	  tmp[strlen(tmp)-1]=0;
	  execute(tmp);
	  if (tmp[0]) gl_histadd(tmp);
	}
      fclose(fp);
    }
}
  

quit(int code)
{
  if (ifalias("autoquit"))
    execute("autoquit");
  exit(code);
}

#if defined(TIOCGWINSZ) && defined(SIGWINCH)

void getwinsz()
{
  struct winsize win;

  ioctl(0,TIOCGWINSZ,&win);
  
  winsize = win.ws_col;
  if (winsize==0) winsize=80;
  gl_setwidth(winsize);
  gl_redraw_r();
}
#endif


load_compctl()
{
  /* Load command(s) */

  static char *sets[] = {
    "frame","timeout","sleep","debug","bin","try","hash",
    "port","prompt","rest","noopinterval","nooptimeout","user",
    "wwwgateway","wwwport",
    NULL};
  char **p;

  CMDS *xcmd = &cmds[0];

  ALIAS *a=firstalias;
  
  while (xcmd->cmd != NULL) load_key(xcmd->cmd),xcmd++;

  /* Load alias(es) */

  while (a!=NULL) load_key(a->name),a=a->next;

  for (p=sets;*p!=NULL;p++)
    {
      FtpString tmp;

      strcpy(tmp,"set ");
      strcat(tmp,*p);
      load_key(tmp);
    }

  load_hosts();
  
}

load_hosts()
{
  FILE *in=fopen(gethostsname(),"r");
  FtpString s,ss;
  
  if (in==NULL) return;

  while ( fgets(s,sizeof s,in)!=NULL)
    {
      s[strlen(s)-1]=0;
      strcpy(ss,"open ");
      strcat(ss,s);
      load_key(ss);
    }

  fclose(in);
}

log_traffic ( char *hf, char *ht, char *ff, char *ft, int counter)
{
  time_t t=time((time_t *)0);
  struct tm *lt=localtime(&t);
  FtpString hostname,txt;
  int fd=open(LOG_TRAFFIC_FILE,O_WRONLY|O_CREAT|O_APPEND);

  if (fd==-1) return;

  while ((status=flock(fd,LOCK_EX)==-1) && errno==EWOULDBLOCK);

  if (status==-1) return;

  gethostname(hostname, sizeof hostname);  
  
  
  sprintf(txt,"%02d:%02d:%02d %02d:%02d:%02d %s@%s %s:%s -> %s:%s %d bytes\n",
	  lt->tm_mday,lt->tm_mon+1,lt->tm_year+1900,
	  lt->tm_hour,lt->tm_min,lt->tm_sec,
	  getpwuid(getuid())->pw_name,hostname, hf, ff, ht, ft, counter) ;

  write(fd,txt,strlen(txt));

  close(fd);
}






\End\Of\Shar\
else
  echo "will not over write ./utils/uftp.c"
fi
if `test ! -s ./utils/uftp.h`
then
echo "writing ./utils/uftp.h"
cat > ./utils/uftp.h << '\End\Of\Shar\'
#include <sys/types.h>
#include <FtpLibrary.h>
#include <strings.h>
#include <setjmp.h>
#include <signal.h>
#include <sys/wait.h>
#include <sys/file.h>
#include <arpa/telnet.h>
#include <sys/ioctl.h>
#include <pwd.h>
#include <errno.h>
#include <glob.h>
#include "getline.h"
#include "list.h"

#define SYSTEMRC "/usr/share/etc/uftprc"
#define LINK ftp[frame]
#define NFRAMES 10
#define TIME(proc) settimer(), status = proc , showtimer(), status
#define ARGS char *w1,char *w2,char *w3,char *w4,char *w5,char *w6
#define NULLARGS "","","","","",""
#define log(x) FtpLog("uftp",x)
#define LOG_TRAFFIC_FILE "/usr/share/etc/uftp.log"

#ifdef MIN
#undef MIN
#endif

#define MIN(x,y) ((x)<(y)?(x):(y))
typedef struct
{
  FtpString host;
  FtpString user;
  FtpString pass;
  FtpString pwd;
  int lock;
} LINKINFO;

typedef struct
{
  char *cmd;
  int (*func)();
  int need;
  char *help;
} CMDS;

typedef struct _alias
{
  FtpString name,str;
  struct _alias *next;
} ALIAS;

typedef struct _token
{
  FtpString word;
  struct _token *next;
  struct _token *shift;
} TOKEN;



extern ALIAS *firstalias;
extern FTP *ftp[NFRAMES];
extern LINKINFO iftp[NFRAMES];
extern LIST *hosts;
extern int frame;
extern int lastcmd;
extern int trymode;
extern int hashmode;
extern int restmode;
extern int sleeptime;
extern int winsize;
extern int interactive;
extern time_t noopinterval,nooptimeout;
extern CMDS cmds[];
extern int status;
extern FtpString prompt;
extern FtpString defaultuser;
extern jmp_buf start;

char *word(char *,int);
char *readline(char *);

#ifndef _AIX
char *getpass(char *);
#endif

char *getrcname();
char *getaliasrcname();
char *getsetsrcname();
char *gethostsname();
char *makestr();
char *expandalias(char *str);
char *getprompt();
char *makefilename(char *,char *);
char *getalias(char *);

int tab_hook(char *,int,int*);
int compctl_hook(char *,int,int*);

int setenv(char *, char*);
int unsetenv(char *);
int printenv();
void getwinsz();

void intr(int);
void noop();
int  setargs(char *);

int myhash(FTP *,unsigned int);
STATUS my_error(FTP *, int, char *);


extern FtpString www_gateway;
extern int www_port;








\End\Of\Shar\
else
  echo "will not over write ./utils/uftp.h"
fi
if `test ! -s ./utils/uftp.man`
then
echo "writing ./utils/uftp.man"
cat > ./utils/uftp.man << '\End\Of\Shar\'
.TH \fBuftp\fR 1
.SH Name
uftp \- universal file transfer program
.SH Syntax
\fBuftp\fR
.PP
\fBuftp alias_or_command [args]\fR
.PP 
\fBuftp hostname\fR

.SH Description


The uftp is user interactive and non-interactive program to the ARPANET File Transfer Protocol (RFC959).
The uftp allows user to transfer files, group of files, threes of directories in foreground and background modes. uftp runs on the client host.


.SH Basic features



Auto retrying of connection to remote node until it is succeeded.
.PP
Automatic reconnection with continue to transfer if the connection was broken.
.PP
Several sessions (frames) simulteniously. Dynamically switching between them. 
.PP
Setup commands, which are executed after "open" and "cd" commands. (aliases autologin & autocd )
.PP
Cleaning timeout on the remote server (by default switched off).
.PP
The user can setup a lot of system parameters, like timeouts, reconnect delays, default port number,
automatic binary mode, automatic "hash" mode and interval to clean timeouts on FTP server. (by default it is turned off)
.PP
User can setup the prompt with the descriptions of local and remote directories, full or short site name, 
time, frame number,
remote user's name, port number, timeout, process identification.
.PP
Several commands in one line (separated by ';').
.PP
Aliases with arguments, which may contain few commands separated by ';'.
.PP
Redirection input/output irrespective of context or/and command.
.PP
The local files are libftp-files, which split on local filesand pipes. This particular file specification
can be used in any context, including for file names of redirection input/output streams.
.PP 
Any command may be executed in background mode, though the current frame is not dropped out and user can continue
his work.
.PP
Creation of alias described the current frame, and save all exist aliases to personal 
automatic startup file, which is differ from startup file. This file user can make himself.
.PP
Completion mechanism which predefined to apply existing commands aliases and known hosts and can be modified by user (using command compctl).
.PP
.PP
.PP
Automatic file-search using archie-server that. 
.PP
Receiving files thru HTTP-proxyserver.
 
.SH Environment description



Command line mode supports all edit key bindings and completion mechanism. (uftp using public domain getline library). 
Before each command user have the prompt with description of current frame.
If debug mode is enabled user see the protocol between uftp and ftp-daemon (ftpd).



.SH Commands


.IP \fBcompctl\fR 10
[arg1] [arg2] [arg3]

This function gives possibility to manage completion of commands typed by user
in command line. Without any argument last one prints list of already 
existed command in compctl-cache. Otherwise you push new one to stack. For 
example you push to lines in your startup files such as "compctl set login albert" and "compctl set login manager", if later you press <TAB> after typing "login" you got list with two words, and after pressing "a" or "m" and <TAB> you will complete one from them.


.IP \fBconnect\fR 10
[host-name]

Connect to remote site.

.IP \fBopen\fR 10
[host-name] [user-name] [password] [directory] 

Makes connection to remote site, sent login, password and change directory. 
If the "try" option is set an attempt to connect will be forced until success.

.IP \fBftp\fR 10
[hostname] [directory]

Anonymous connection to ftp-site.

.IP \fBreopen\fR 10

Reopen broken frame. 

.IP \fBpreconnect\fR 10
[host-name] [user-name] [password] [directory] 

Makes data for future connection in the cache. If after that you will do some
transfer operation like get or put .... connection will be maded just before 
transfer, it can be usable if you don't have access to server right now and
want to keep task for execution at time when it will be possible.

.IP \fBclose\fR 10

Close the current connection.

.IP \fBquit\fR 10
.IP \fBexit\fR 10

Quit from uftp (You can press Control-D)

.IP \fBlist\fR 10

Description list of all frames.

.IP \fBuser\fR 10
[user-name] [password]

Send user's name to site, automatically require password if needed.

.IP \fBpass\fR 10
[password]

Specify user's password.

.IP \fBbin\fR 10

Set binary transfer mode.

.IP \fBascii\fR 10

Set ASCII transfer mode.

.IP \fBcd\fR 10
directory_name

Change the current directory on remote site.

.IP \fBacd\fR 10
[directory_name]

Archie searching by specified pattern with subsequent connection to desired point. 
Maximum number of possible points is 20.
Afterwards user can select any point. If user don't specify "directory_name", 
last search buffer is displayed for selection, if this buffer is not empty.

.IP \fBlcd\fR 10
directory_name

Local change directory. User can use meta-characters.

.IP \fBabort\fR 10

Abort execute of last procedure with server. 

.IP \fBmkdir\fR 10
directory_name

Create new directory on the server.

.IP \fBrm\fR 10
filename_or_pattern

Remove specified file(s) on the server.

.IP \fBmv\fR 10
old_filename new_filename

Move file on the server.

.IP \fBdir\fR 10
[keys] [filename_spec] ....

Make long list of specified file(s) with date, size, etc...

.IP \fBls\fR 10
[keys] [filename_spec] ....

Make short list of specified file(s).

.IP \fBget\fR 10
remote_filename [local_filename_or_directory]

Receive the file from the server to local file system (only one file!).
If option "rest" is turn on then transfer starts from the end of local file.

.IP \fBpget\fR 10
HTTP-file-specification

Receive the file using HTTPD as gateway for transferring. Using that you can transfer files (ftp://hostname/path/file) or other objects (for example: http://www.cern.ch). For setting http-server and his port will use "set wwwgateway" and "set wwwport"

.IP \fBmget\fR 10
[remote_filename] [local_directory]

Receive many files from the server to local file system. If you run this command without arguments you will transfer all files in that directory including subdirectories and his files.

.IP \fBaget\fR 10
[pattern_for_archie]

Getting the file, which need to find via archie service. See also "acd" description.

.IP \fBaaget\fR 10
[pattern_for_archie]

Getting the file, which need to find via archie service and retrieving it
from any host which accessible. If connection to server will broke then 
reconnect to another server or some if only one and continue to transfer 
from last point. 

.IP \fBput\fR 10
local_filename [remote_filename]

Put one file to server.

.IP \fBmput\fR 10
local_filename(s) 

Put specified file(s) to server.

.IP \fBcopy\fR 10
[frame/]filename [frame/]filename

Copy one file from first frame to second. If the frame number is not specified 
then use current frame number. Transfer operation executes via libftp cache.


.IP \fBccopy\fR 10
[frame/]filename [frame/]filename

Copy one file from first frame to second. If the frame number is not specified 
then used current frame number. Transfer operation executes via leased line
between two servers cache.


.IP \fBcat\fR 10
filename

Display context of specified file on screen. For display file with 
page-scrolling you can define once command for example "less" as "alias less get $1 |less".  

.IP \fBpage\fR 10

The same as cat, but with using of pager. Name of pager specified in environment variable
PAGER or "more" by default. (It's just predefined possibility described bellow)

.IP \fBbg\fR 10
any_command

any_command &

Run any command in background mode. Default output is redirected to
/tmp/uftp-<user_name>/XXXXXX file.

.IP \fBarchie\fR 10
[pattern]

Archie search. In case if argument are omitted, reprint last search. (same for acd and aget)

.IP \fBdup\fR 10

Create new frame as current.

.IP \fBquote\fR 10

Send raw command to server. If option "glassmode" is set then all non-recognized
commands send to server as raw also.

.IP \fBhelp\fR 10
[command]

Print brief help or help for specified command.

.IP \fBsetenv\fR 10
variable_name value

Setup system's environment variable.
 
.IP \fBunsetenv\fR 10
variable_name

Remove system's environment variable.
 
.IP \fBenv\fR 10

Print system's environment variables.

.IP \fBalias\fR 10
alias_name alias_string

Makes new alias, if the alias string uftp contains string like $1, $2, $* then
it will be replaced by argument to alias. If this sequences in alias is not found, then 
all existing alias's arguments will append to end of alias call string. User can insert to alias
string like \\\> \\\< for future redirect input/output. Quotes ' and " can be used 
also.

.IP \fBunalias\fR 10
alias_name 

Remove specified alias.

.IP \fBmkalias\fR 10
alias_name

Makes new alias, which user can use in future for login to this point again. 
See also "savealiases"

.IP \fBsavealiases\fR 10

Save all aliases in startup file.


.SH Libftp file specification

All local files interpret as libftp's files. Libftp responds to two types of files such
 as local file and program
pipes. All files can be described as next syntax:

   |string - interprets string as shell command, which must be \
   executed with appropriate input/output for file. It depends where
   this file is specified.

   *STDIN*, *STDOUT*, *STDERR* or char '-' - opened streams.

   anything - local file name.

.SH String syntax

The strings starting from char '!' interpret as shell command.
The strings or aliases containing one or few char ';' will be executed as a chain commands.
The chains of characters between  " or ' interpret as one set without syntax resolving.
In any command string user can redirect input or/and output 
using char > and < . For the complex file name it must quoted by ' or ". 

Examples:

	dir >filename

	cat filename >'|mail -s "my files" fiend@hostname.domain' 

	dir -R etc bin >"|gzip >result.gz"

	put - < "|finger @hostname" newfile.finger


.SH Options (command set)

.IP \fBset\fR


Show all current settings.

.IP \fBset\fR 
frame <frame_number>

Switch to another frame. You can also switch by insert on frame number to the 
begin of command line.

.IP \fBset\fR 
timeout <seconds> 

Set timeout for send/receive operations.

.IP \fBset\fR 
noop <secs> 

Set interval for send NOOP command to each connected server for cleaning
timeouts. 

.IP \fBset\fR 
nooptimeout <seconds> 

Set timeout for NOOP operation.

.IP \fBset\fR 
sleep <secs> 

Set pause interval between transfer attempts.

.IP \fBset\fR 
debug <y|n> 

Enable or disable protocol debug output

.IP \fBset\fR 
try <y|n> 

Enable or disable retrys after lost peer.

.IP \fBset\fR 
hash <y|n> 

Enable or disable trace for the transfer operations.

.IP \fBset\fR 
restore <y|n> 

Enable or disable default transfer starting from end of file.

.IP \fBset\fR 
bin <y|n> 

Automatic binary mode.

.IP \fBset\fR 
prompt <prompt_string> 

Set the prompt. Prompt is a string, which may contain %<char>
or ^<char> combitanions with the next embodies:

               %H, %h - full and short remote host names
               %M, %m - full and short local host names
               %u     - remote user's name
               %d     - remote current directory
               %D     - local current directory
               %f     - number of current frame
               %p     - the ftp's port number
               %t     - timeout
               %T     - current time
               %P     - uftp process id
               %%     - character %
               ^<char>- control character
               %^     - character ^


.IP \fBset\fR 
port <number>

Set default FTP's port for next sessions.

.IP \fBset\fR 
user <user_name>

Set default user's name.

.IP \fBset\fR 
wwwgateway <host_name>

Set www proxy gateway name or it's address.

.IP \fBset\fR 
wwwport <port_number>

Set port number for http-proxy server.

.IP \fBsavesets\fR 

Save all sets to startup file.

.SH Startup file

User can modify his startup file created automatically. This file may
contain some uftp's commands separated by new-line. The name of this file is ~/.uftprc.
The file ~/.uftp_aliases ~/.uftp_sets is created automatically by uftp's command "savealias" or "savesets", 
so it is not needed to edit handy.
The file ~/.uftp_hosts can contain list of hosts for uftp's command which have host's name as argument.

.SH Author of uftp and libftp

  Oleg Orel 	

  Europien Organization for Nuclear Research
  Geneva, SWITZERLAND

  E-mail: orel@dxunk1.cern.ch



.SH See also (and compare)

 \fBncftp\fR (1),  \fBftp\fR (1),  \fBftpd\fR (8)
\End\Of\Shar\
else
  echo "will not over write ./utils/uftp.man"
fi
echo "Finished archive 4 of 8"
exit



