[comp.sources.amiga] v90i053: uucp 1.03D - unix compatible uucp/mail/news system, Part09/16

Amiga-Request@cs.odu.edu (Amiga Sources/Binaries Moderator) (02/04/90)

Submitted-by: overload!dillon (Matt Dillon)
Posting-number: Volume 90, Issue 053
Archive-name: unix/uucp-1.03d/part09

#!/bin/sh
# This is a shell archive.  Remove anything before this line, then unpack
# it by saving it into a file and typing "sh file".  To overwrite existing
# files, type "sh file -c".  You can also feed this as standard input via
# unshar, or by typing "sh <file", e.g..  If this archive is complete, you
# will see the following message at the end:
#		"End of archive 9 (of 16)."
# Contents:  man/MAPS src/News060/rnews.c src/getty/getty.c
# Wrapped by tadguy@xanth on Sat Feb  3 20:51:15 1990
PATH=/bin:/usr/bin:/usr/ucb ; export PATH
if test -f 'man/MAPS' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'man/MAPS'\"
else
echo shar: Extracting \"'man/MAPS'\" \(18172 characters\)
sed "s/^X//" >'man/MAPS' <<'END_OF_FILE'
X# The UUCP map is posted to newsgroup comp.mail.maps.
X#
X# From rn, the map can be easily unpacked with a command such as
X#	43-46w | (cd ~uucp/uumap ; sh)
X# or you can use John Quarterman's script to automatically unpack the files.
X# All files intended as pathalias input being with "d." and "u.", thus
X#	pathalias Path.* uumap/[du].*
X# is a useful command to run.  (You supply Path.* with local additions.)
X# 
X# The map is also available on a demand basis at a number of hosts who
X# have volunteered to make their copy available to the general public ;
X# details of this access are posted separately in file "network".
X# 
X# The files are organized by country, using the ISO 3166 3 letter country
X# code for each country.  Each file has a name like u.iso.r1.r2.s, where
X# "iso" is the country code, r1, r2, etc are regions and subregions
X# (e.g. states in the USA, provinces in Canada, etc.) and s is a sequence
X# number (usually 1, but sometimes 2, 3, and up may be provided to keep
X# individual files down to a reasonable size, thus, u.usa.ca is separated
X# into two regions: [135] for southern, [246] for northern.)
X#
X# The map contains two types of files: u.* and d.* files.  The d.* files
X# are for domains registered in the UUCP Zone.  The u.* files are for
X# UUCP hosts that do not have officially registered domains, but rather
X# belong to the unofficial ".UUCP domain".  Membership in the UUCP Zone
X# allows organizations and individuals to register official, unique,
X# domain names, recognized by all major academic computing networks
X# worldwide.  For more information about joining the UUCP Zone, send
X# electronic mail to the UUCP Project at one of the addresses
X#	uucp-query@stargate.com
X#	{uiucdcs,rutgers}!stargate!uucp-query
X#	stargate!uucp-query@rutgers.edu
X# or, if you cannot send electronic mail, telephone
X#	+1 213 868 1134
X# We strongly encourage you to send email if at all possible, since it
X# cuts down on telephone tag and is much more efficient on our volunteer
X# workforce.
X# 
X# This map can be used to generate mail routes with pathalias.  Pathalias
X# was posted to Usenet in January 1986 and will be posted again as needed
X# The map is also useful to determine the person to contact when a problem
X# arises, and to find someone for a new site to connect to.
X# 
X# Please check the entry for your host (and any neighbors for whom you know
X# the information and have the time) for correctness and completeness.
X# Please send corrections and additional information to uucpmap@rutgers.UUCP
X# or rutgers!uucpmap or uucpmap@rutgers.EDU.
X# 
X# This map is maintained by a group of volunteers, making up part of the UUCP
X# Project.  These people devote many hours of their own time to helping out
X# the UUCP community by keeping this map up to date.  The volunteers include:
X#
X#
X# Jeff Janock - jeff@necntc.nec.com
X#   USA: Conneticut, Maine, Massachusetts, New Hampshire, Rhode Island, Vermont
X#
X#
X# Nicholas (Nike) Horton - horton@reed.uucp
X#   USA: Delaware, Maryland, New Jersey, Virginia, Washington DC,
X#   West Virginia
X# 
X#
X# Rayan Zachariassen rayan@ai.toronto.edu
X#   CANADA: All provinces
X# 
X#
X# Bill Blue - bblue@crash.uucp
X#   USA: Arizona, California (Southern half)
X# 
X#
X# Erik Fair - nca-maps@ucbvax.berkeley.edu
X#   USA: California (Northern half)
X# 
X# 
X# David Schmidt - davids@iscuva.iscs.com
X#   USA: Alaska, Idaho, Oregon, Montana, Washington, Wyoming
X# 
X# 
X# Doug McCallum - dougm@ico.isc.com
X#   USA: Arkansas, Colorado, Iowa, Kansas, Louisiana, Mississippi, Nebraska,
X#        New Mexico, Oklahoma, Texas, Utah
X# 
X#
X# Piet Beertema - Europe (piet@cwi.nl)
X#   Europe: all countries (unless otherwise noted)
X# 
X# 
X# Mikel Manitius - map-request@codas.att.com
X#   USA: Florida
X#
X#
X# Jeff Lee - jeff@ics.gatech.edu
X#   USA: Georgia
X# 
X# 
X# Tim Thompson - tgt@stargate.com
X#   USA: Ohio
X#
X#
X# Dave Zimmerman - dpz@rutgers.edu
X#   USA: Alabama, Illinois, Indiana, North Carolina, North Dakota,
X#        Pennsylvania, South Carolina, South Dakota, Tennessee, Wisconsin
X#
X#
X# Bob Leffler - bob@rel.eds.com
X#   USA: Michigan
X#
X#
X# Rob Robertson - rob@philiabs.philips.com
X#   USA: New York
X#
X#
X# Hokey - hokey@plus5.com
X#   USA: Missouri
X# 
X#
X# David Herron - david@e.ms.uky.edu
X#   USA: Kentucky
X# 
X#
X# Brian Richter - brianr@rosevax.rosemount.com
X#   USA: Minnesota
X# 
X#
X# Torben Nielson, Bob Cunningham - torben@uhmanoa.UUCP, bob@uhmanoa.UUCP
X#   USA: Hawaii
X# 
X#
X# Haesoon Cho - nmc@sorak.kaist.ac.kr
X#   Korea: all regions
X# 
X#
X# Tohru Asami - asami@kddspeech.kddlabs.jp
X#   Japan: all regions
X# 
X#
X# Robert Elz, Dave Davey - map-coord@munnari.UUCP
X#   Australia: all regions
X# 
X#
X# Paul Graham - pjg@unrvax.unr.edu
X#   USA: Nevada
X#
X#
X# Susana Lilik - susana@indovax.uucp
X#   Indonesia: all regions
X#
X#
X# Mel Pleasant - pleasant@rutgers.edu
X#   Singapore: all regions
X#   New Zealand: all regions
X# 
X# 
X
X# Please note that the purpose of this map is to make routers within
X# UUCP work.  The eventual direction is to make the map smaller (through
X# the use of domains), not larger.  As such, sites with lots of local
X# machines connected together are encouraged to create a few gateway
X# machines and to make arrangements that these gateways can forward mail
X# to your local users.  We would prefer not to have information listing
X# the machines on your local area networks, and certainly not your
X# personal computers and workstations.  If you need such information for
X# local mail delivery, create a supplement in pathalias form which you
X# do not publish, but which you combine with the published data when you
X# run pathalias.  We also do not want information about machines which
X# are not on UUCP, that is, which are not reachable with the ! notation
X# from the main UUCP cluster.
X#
X# If you don't have pathalias, it has been posted to mod.sources most
X# recently in October 1987.  If you don't have access to a mod.sources
X# archive, contact the mod.sources moderator (currently Rich Salz,
X# sources@uunet.uu.net.)
X# 
X# The remainder of this file describes the format of the UUCP map data.
X# It was written July 9, 1985 by Erik E. Fair <ucbvax!fair>, and last
X# updated July 12, 1985 by Mark Horton <stargate!mark>.
X# 
X# The entire map is intended to be processed by pathalias, a program
X# that generates UUCP routes from this data.  All lines beginning in `#'
X# are comment lines to pathalias, however the UUCP Project has defined a
X# set of these comment lines to have specific format so that a complete
X# database could be built.
X# 
X# The generic form of these lines is
X# 
X# #<field id letter><tab><field data>
X# 
X# Each host has an entry in the following format.  The entry should
X# begin with the #N line, end with a blank line after the pathalias
X# data, and not contain any other blank lines, since there are ed, sed,
X# and awk scripts that use expressions like /^#N $1/,/^$/ for the
X# purpose of separating the map out into files, each containing one site
X# entry.
X# 
X# #N	UUCP name of site
X# #S	manufacturer machine model; operating system & version
X# #O	organization name
X# #C	contact person's name
X# #E	contact person's electronic mail address
X# #T	contact person's telephone number
X# #P	organization's address
X# #L	latitude / longitude
X# #R	remarks
X# #U	netnews neighbors
X# #W	who last edited the entry ; date edited
X# #
X# sitename	.domain
X# sitename	remote1(FREQUENCY), remote2(FREQUENCY),
X# 	remote3(FREQUENCY)
X# 
X# Example of a completed entry:
X# 
X# #N	ucbvax
X# #S	DEC VAX-11/750; 4.3 BSD UNIX
X# #O	University of California at Berkeley
X# #C	Robert W. Henry
X# #E	ucbvax!postmaster
X# #T	+1 415 642 1024
X# #P	573 Evans Hall, Berkeley, CA 94720
X# #L	37 52 29 N / 122 13 44 W
X# #R	This is also UCB-VAX.BERKELEY.EDU [10.2.0.78] on the internet
X# #U	decvax ibmpa ucsfcgl ucbtopaz ucbcad
X# #W	ucbvax!fair (Erik E. Fair); Sat Jun 22 03:35:16 PDT 1985
X# #
X# ucbvax	.ucbvax.Berkeley.EDU
X# ucbvax	decvax(DAILY/4), ihnp4(DAILY/2),
X# 	sun(POLLED)
X# 
X# Specific Field Descriptions
X# 
X# #N	system name
X# 
X# Your system's UUCP name should go here. Either the uname(1) command
X# from System III or System V UNIX; or the uuname(1) command from
X# Version 7 UNIX will tell you what UUCP is using for the local UUCP
X# name.
X# 
X# One of the goals of the UUCP Project is to keep duplicate UUCP host
X# names from appearing because there exist mailers in the world which
X# assume that the UUCP name space contains no duplicates (and attempts
X# UUCP path optimization on that basis), and it's just plain confusing
X# to have two different sites with the same name.
X# 
X# At present, the most severe restriction on UUCP names is that the name
X# must be unique somewhere in the first six characters, because of a
X# poor software design decision made by AT&T for the System V release of
X# UNIX.
X# 
X# This does not mean that your site name has to be six characters or
X# less in length. Just unique within that length.
X# 
X# With regard to choosing system names, HARRIS'S LAMENT:
X# 
X# 	``All the good ones are taken.''
X# 
X# #S	machine type; operating system
X# 
X# This is a quick description of your equipment. Machine type should be
X# manufacturer and model, and after a semi-colon(;), the operating
X# system name and version number (if you have it). Some examples:
X# 
X# 	DEC PDP-11/70; 2.9 BSD UNIX
X# 	DEC PDP-11/45; ULTRIX-11
X# 	DEC VAX-11/780; VMS 4.0
X# 	SUN 2/150; 4.2 BSD UNIX
X# 	Pyramid 90x; OSx 2.1
X# 	CoData 3300; Version 7 UniPlus+
X# 	Callan Unistar 200; System V UniPlus+
X# 	IBM PC/XT; Coherent
X# 	Intel 386; XENIX 3.0
X# 	CRDS Universe 68; UNOS
X# 
X# #O	organization name
X# 
X# This should be the full name of your organization, squeezed to fit
X# inside 80 columns as necessary. Don't be afraid to abbreviate where
X# the abbreviation would be clear to the entire world (say a famous
X# institution like MIT or CERN), but beware of duplication (In USC the C
X# could be either California or Carolina).
X# 
X# #C	contact person
X# 
X# This should be the full name (or names, separated by commas) of the
X# person responsible for handling queries from the outside world about
X# your machine.
X# 
X# #E	contact person's electronic address
X# 
X# This should be just a machine name, and a user name, like
X# `ucbvax!fair'. It should not be a full path, since we will be able to
X# generate a path to the given address from the data you're giving us.
X# There is no problem with the machine name not being the same as the #N
X# field (i.e. the contact `lives' on another machine at your site).
X# 
X# Also, it's a good idea to give a generic address or alias (if your
X# mail system is capable of providing aliases) like `usenet' or
X# `postmaster', so that if the contact person leaves the institution or
X# is re-assigned to other duties, he doesn't keep getting mail about the
X# system. In a perfect world, people would send notice to the UUCP
X# Project, but in practice, they don't, so the data does get out of
X# date. If you give a generic address you can easily change it to point
X# at the appropriate person.
X# 
X# Multiple electronic addresses should be separated by commas, and all
X# of them should be specified in the manner described above.
X# 
X# #T	contact person's telephone number
X# 
X# Format: +<country code><space><area code><space><prefix><space><number>
X# 
X# Example:
X# 
X# #T	+1 415 642 1024
X# 
X# This is the international format for the representation of phone
X# numbers. The country code for the United States of America (and
X# Canada) is 1. Other country codes should be listed in your telephone
X# book.
X# 
X# If you must list an extension (i.e. what to ask the receptionist for,
X# if not the name of the contact person), list it after the main phone
X# number with an `x' in front of it to distinguish it from the rest of
X# the phone number.
X# 
X# Example:
X# 
X# #T	+1 415 549 3854 x37
X# 
X# Multiple phone numbers should be separated by commas, and all of them
X# should be completely specified as described above to prevent
X# confusion.
X# 
X# #P      organization's address
X# 
X# This field should be one line filled with whatever else anyone would
X# need after the contact person's name, and your organization's name
X# (given in other fields above), to mail you something by paper mail.
X# 
X# #L      latitude and longitude
X# 
X# This should be in the following format:
X# 
X# #L	DD MM [SS] "N"|"S" / DDD MM [SS] "E"|"W" ["city"]
X# 
X# Two fields, with optional third.
X# 
X# First number is Latitude in degrees (NN), minutes (MM), and seconds
X# (SS), and a N or S to indicate North or South of the Equator.
X# 
X# A Slash Separator.
X# 
X# Second number is Longitude in degrees (DDD), minutes (MM), and seconds
X# (SS), and a E or W to indicate East or West of the Prime Meridian in
X# Greenwich, England.
X# 
X# Seconds are optional, but it is worth noting that the more accurate
X# you are, the more accurate maps we can make of the network (including
X# blow-ups of various high density areas, like New Jersey, or the San
X# Francisco Bay Area).
X# 
X# If you give the coordinates for your city (i.e. without fudging for
X# where you are relative to that), add the word `city' at the end of the
X# end of the specification, to indicate that. If you know where you are
X# relative to a given coordinate for which you have longitude and
X# latitude data, then the following fudge factors can be useful:
X# 
X# 1 degree	=	69.2 miles	=	111 kilometers
X# 1 minute	=	1.15 miles	=	1.86 kilometers
X# 1 second	=	102 feet	=	30.9 meters
X#
X# For LONGITUDE, multiply the above numbers by the cosine of your
X# latitude.  For instance, at latitude 35 degrees, a degree of longitude
X# is 69.2*0.819 = 56.7 miles; at latitude 40 degrees, it is 69.2*0.766 =
X# 53.0 miles.  If you don't see why the measure of longitude depends on
X# your latitude, just think of a globe, with all those N-S meridians of
X# longitude converging on the poles.  You don't do this cosine
X# multiplication for LATITUDE.
X#
X# Here is a short cosine table in case you don't have a trig calculator
X# handy.  (But you can always write a short program in C.  The cosine
X# function in bc(1) doesn't seem to work as documented.)
X# deg  cos  deg  cos  deg  cos  deg  cos  deg  cos  deg  cos
X#  0  1.000  5  0.996 10  0.985 15  0.966 20  0.940 25  0.906
X# 30  0.866 35  0.819 40  0.766 45  0.707 50  0.643 55  0.574
X# 60  0.500 65  0.423 70  0.342 75  0.259 80  0.174 85  0.087
X# 
X# The Prime Meridian is through Greenwich, England, and longitudes run
X# from 180 degrees West of Greenwich to 180 East.  Latitudes run from
X# 90 degrees North of the Equator to 90 degrees South.
X# 
X# #R      remarks
X# 
X# This is for one line of comment. As noted before, all lines beginning
X# with a `#' character are comment lines, so if you need more than one
X# line to tell us something about your site, do so between the end of the
X# map data (the #?\t fields) and the pathalias data.
X# 
X# #U	netnews neighbors
X# 
X# The USENET is the network that moves netnews around, specifically,
X# news.announce.important. If you send news.announce.important to any of
X# your UUCP neighbors, list their names here, delimited by spaces.
X# Example:
X# 
X# #U	ihnp4 decvax mcvax seismo
X# 
X# Since some places have lots of USENET neighbors, continuation lines
X# should be just another #U and more site names.
X# 
X# #W      who last edited the entry and when
X# 
X# This field should contain an email address, a name in parentheses,
X# followed by a semi-colon, and the output of the date program.
X# Example:
X# 
X# #W	ucbvax!fair (Erik E. Fair); Sat Jun 22 03:35:16 PDT 1985
X# 
X# The same rules for email address that apply in the contact's email
X# address apply here also. (i.e. only one system name, and user name).
X# It is intended that this field be used for automatic ageing of the
X# map entries so that we can do more automated checking and updating
X# of the entire map. See getdate(3) from the netnews source for other
X# acceptable date formats.
X# 
X# PATHALIAS DATA (or, documenting your UUCP connections & frequency of use)
X# 
X# The DEMAND, DAILY, etc., entries represent imaginary connect costs (see
X# below) used by pathalias to calculate lowest cost paths.  The cost
X# breakdown is:
X# 
X# 	LOCAL		25	local area network
X# 	DEDICATED	95	high speed dedicated
X# 	DIRECT		200	local call
X# 	DEMAND          300     normal call (long distance, anytime)
X# 	HOURLY		500	hourly poll
X# 	EVENING		1800	time restricted call
X# 	DAILY		5000	daily poll
X# 	WEEKLY		30000	irregular poll
X# 	DEAD            a very high number - not usable path
X# 
X# Additionally, HIGH and LOW (used like DAILY+HIGH) are -5 and +5
X# respectively, for baud-rate or quality bonuses/penalties.  Arithmetic
X# expressions can be used, however, you should be aware that the results
X# are often counter-intuitive (e.g. (DAILY*4) means every 4 days, not 4
X# times a day).  This is because the numbers represent "cost of connection"
X# rather than "frequency of connection."
X# 
X# The numbers are intended to represent cost of transferring mail over
X# the link, measured very rougly in elapsed time, which seems to be
X# far more important than baud rates for this type of
X# traffic.  There is an assumed high overhead for each hop; thus,
X# HOURLY is far more than DAILY/24.
X# 
X# There are a few other cost names that sometimes appear in the map.
X# Some are synonyms for the preferred names above (e.g. POLLED is assumed
X# to mean overnight and is taken to be the same as DAILY), some are
X# obsolete (e.g.  the letters A through F, which are letter grades for
X# connections.) It is not acceptable to make up new names or spellings
X# (pathalias gets very upset when people do that...).
X# 
X# LOCAL AREA NETWORKS
X# 
X# We do not want local area network information in the published map.
X# If you want to put your LAN in your local Path.* files, read about
X# the LAN syntax in the pathalias.1 manual page.
X# 
X# WHAT TO DO WITH THIS STUFF
X# 
X# Once you have finished constructing your pathalias entry, mail it off
X# to {uunet|gatech|ucsd|ames}!rutgers!uucpmap, which will be sent to the
X# appropriate regional map coordinator.  They maintain assigned
X# geographic sections of the map, and the entire map is posted on a
X# rolling basis in the USENET newsgroups comp.mail.maps over the course
X# of a month.
X# 
X# Questions or comments about this specification should also be directed
X# at rutgers!uucpmap.
X# 
END_OF_FILE
if test 18172 -ne `wc -c <'man/MAPS'`; then
    echo shar: \"'man/MAPS'\" unpacked with wrong size!
fi
# end of 'man/MAPS'
fi
if test -f 'src/News060/rnews.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'src/News060/rnews.c'\"
else
echo shar: Extracting \"'src/News060/rnews.c'\" \(17361 characters\)
sed "s/^X//" >'src/News060/rnews.c' <<'END_OF_FILE'
X
X/*
X *  RNEWS.C
X *
X *  Copyright 1988 by William Loftus.  All rights reserved.
X *  (C) Copyright 1989-1990 by Matthew Dillon,  All Rights Reserved.
X *
X *  Version 0.60 Beta
X *
X *  This is rnews for compressed news.	News 2.11 will uux
X *  a file to this system that will be in compressed format.
X *  This program will strip off the "#! cunbatch", uncompress
X *  the news, and call unbatch.  If the news is not in compressed
X *  format it will just pass it to unbatch.
X */
X
X#include <stdio.h>
X#include <stdlib.h>
X#include <string.h>
X#include <log.h>
X#include "/version.h"
X#include <errno.h>
X#include <ctype.h>
X
X#define MAXGROUPS   1024
X
XIDENT(".01");
X
Xvoid uncompress_news();
Xvoid unbatch_news();
Xvoid writeerr();
Xvoid CreateDirsFor();
Xchar *TmpFileName();
X
Xint	Compressed;
Xchar	*SeqFile = "UULIB:SeqNews";
Xchar	*TmpFile;
X
Xvoid
Xmain (argc, argv)
Xint argc;
Xchar **argv;
X{
X    FILE *fp;
X    char buf[32];
X    int ch;
X    int status = 0;
X
X    LogProgram = "RNews";
X    TmpFile = TmpFileName("T:mail");
X
X    fclose(stderr);  /* Assume we are running in the background */
X
X    fgets(buf, 13, stdin);
X
X    if (!strncmp(buf,"#! cunbatch", 11)) {
X	Compressed = 1;
X    } else if (!strncmp(buf,"#! rnews", 8)) {
X	fp = fopen(TmpFile, "w");
X	if (fp == (char *)NULL) {
X	    ulog(-1, "Unable to open %s for output", TmpFile);
X	    exit(1);
X	}
X	fprintf(fp, "%s\n",  buf);
X	while ((ch = getchar()) != EOF) {
X	    fputc(ch, fp);
X	}
X	fclose(fp);
X	Compressed = 0;
X    } else {
X	ulog(-1, "News in wrong format");
X	remove(TmpFile);
X	exit(1);
X    }
X
X    if (Compressed) {
X	uncompress_news();
X    }
X
X    unbatch_news();
X
X    status = remove(TmpFile);
X
X    if (status != 0) {
X	ulog(-1, "Unable to remove %s", TmpFile);
X	exit(1);
X    }
X}
X
X/*
X * Unbatch, an Amiga unbatcher.
X *
X *  Written by Fred Cassirer, 10/8/88.
X *  Some unbatch code originally taken from News2.11 sources.
X *
X *  Ported to Lattice 5.0
X *  Added config.h file
X *  Added use of ERRORFILE
X *  11/23/1988	Dan 'Sneakers' Schein
X *
X * This code (as well as the unbatch stuff) is free for anyone who thinks
X * they can get some use out of it.
X *
X *  Articles will only
X *  be placed in newsgroups as defined in the "UULIB:NewsGroups"
X *  control file.  Articles which are not listed in the control file
X *  are placed in the "Reject" directory.  Articles are sequenced by
X *  the sequencer in "UULIB:seqnews". This could possibly be updated
X *  to use a sequencer within each of the subdirectories, to more closely
X *  resemble the News system under Unix.
X *
X *  Unbatch will also take command line args of files to be unbatched.
X *  Files on the command line are not removed, they should be removed
X *  after running unbatch.
X */
X
Xstatic char *groups[MAXGROUPS];
X
Xvoid
Xunbatch_news()
X{
X    if (!freopen(TmpFile, "r", stdin)) {
X	ulog(-1, "Can't unbatch, expected file %s to exist", TmpFile);
X	exit(1);
X    }
X    if (unbatch())
X	ulog(-1, "Unbatch failed");
X    fclose(stdin);
X}
X
X
Xvoid
Xinitgroups(newsgroups)
Xchar *newsgroups;
X{
X    static char buf[BUFSIZ];
X    static int initflag = 0;
X    int i, len;
X    FILE *fp;
X
X    if (initflag)
X	return;
X
X    initflag = 1;
X
X    if ((fp = fopen(newsgroups, "r")) == NULL)  {
X	ulog(-1, "Unable to open newsgroup file %s", newsgroups);
X	remove(TmpFile);
X	exit(2);
X    }
X
X    i = 0;
X    while (fgets(buf, BUFSIZ, fp) && (i < MAXGROUPS)) {
X	len = strlen(buf) - 1;
X	buf[len] = '\0';
X	if ((groups[i] = malloc(len+1)) == NULL) {
X	    ulog(-1, "malloc failed");
X	    remove(TmpFile);
X	    exit(4);
X	}
X	strcpy(groups[i], buf);
X	i++;
X    }
X    groups[i] = NULL;
X    fclose(fp);
X}
X
Xchar *
Xfinddir(dir)
Xchar *dir;
X{
X    static char work[40];
X    char *p;
X    int i;
X
X    while (1) {
X	p = work;
X	while ((*dir != ' ') && (*dir != ',') && *dir)
X	    *p++ = *dir++;
X
X	*p = '\0';
X	for (i = 0; groups[i] != NULL; ++i) {
X	    if (strcmpi(groups[i], work) == 0)
X		return(work);
X	}
X
X	if (!*dir++)
X	    break;
X    }
X    return(NULL);
X}
X
X/*
X * unbatchnews: extract news in batched format and process it one article
X * at a time.  The format looks like
X *	#! rnews 1234
X *	article containing 1234 characters
X *	#! rnews 4321
X *	article containing 4321 characters
X *
X *	or
X *
X *	#! command [args]
X *	calls LIBDIR/command [args] to process the news
X */
X
X#define MAXARGS 	20
X#define FILES_PER_LINE	5
X
Xchar buf[BUFSIZ];
Xchar homedir[128];
X
Xunbatch()
X{
X    int c;
X    FILE *pfn;
X    long size;
X    FILE *seq;
X    char filename[128], tmpname[30];
X    int  seqno, fcount;
X    long atol();
X    char *dir;
X
X    initgroups("UULIB:Newsgroups");
X
X    if ((seq = fopen(SeqFile, "r")) != NULL) {
X	fscanf(seq,"%d",&seqno);
X	fclose(seq);
X    } else {
X	ulog(-1, "Sequence file %s not found, creating", SeqFile);
X	seqno = 0;
X    }
X
X    if (gets(buf) == NULL) {
X	ulog(-1, "Empty file!");
X	return(2);
X    }
X
X    if (strncmp(buf, "#! rnews ", 9) != 0) {
X	ulog(-1, "unbatch: rnews cmd not supported '%s'!", buf);
X	return(1);
X    }
X
X    fcount = 0;
X    do {
X	if (!fcount) {
X	    fflush(stdout);
X	    fcount = FILES_PER_LINE;
X	}
X	while (strncmp(buf, "#! rnews ", 9) && strncmp(buf, "! rnews ", 8)) { /* kludge for bug */
X	    char *cp;
X	    for (cp = buf; *cp != '\0'; ++cp) {
X		if (!isascii(*cp) || (!isprint(*cp) && !isspace(*cp)))
X		    *cp = '?';
X	    }
X	    ulog(-1, "out of sync, skipping %s", buf);
X	    if (gets(buf) == NULL)
X		return(0);
X	}
X
X	size = atol(buf + (buf[0] == '#' ? 9 : 8));
X	if (size <= 0) {
X	    ulog(-1, "nonsense size %ld", size);
X	    continue;
X	}
X
X	sprintf(tmpname,"UUNEWS:Tmp%d", ++seqno);
X	fcount--;
X	fflush(stdout);
X	if ((pfn = fopen(tmpname, "w")) == NULL) {
X	    ulog(-1, "Can't create %s", tmpname);
X	    exit(3);
X	}
X
X	while (gets(buf)) {
X	    fprintf(pfn, "%s\n", buf);
X	    size -= strlen(buf) + 1;
X	    if (strncmp(buf, "Newsgroup", 9) == 0) {
X		if (!(dir = finddir(buf+12))) {
X		    ulog(-1, "Articles for %s placed in Rejects", buf+12);
X		    dir = "Rejects";
X		}
X		sprintf(filename, "UUNEWS:%s/%d", dir, seqno);
X		break; /* Get out of the while */
X	    }
X	}
X
X	if (size > 0) {
X	    while(--size >= 0 && (c = getc(stdin)) != EOF)
X		putc(c, pfn);
X	    if (ferror(pfn) || fclose(pfn)) {   /* disk full? */
X		ulog(-1, "Error writing to temp file");
X		break;
X	    }
X	    if (rename(tmpname, filename) < 0) {
X		CreateDirsFor(filename);
X		rename(tmpname, filename);
X	    }
X	}
X
X       /*
X	* If we got a truncated batch, don't process the
X	* last article; it will probably be received again.
X	*/
X
X	if (size > 0) {
X	    ulog(-1, "truncated batch");
X	    break;
X	}
X    } while (gets(buf) != NULL);
X
X    if ((seq = fopen(SeqFile, "w")) == NULL) {
X	ulog(-1, "Unable to create %s", SeqFile);
X    }
X    fprintf(seq, "%d\n", seqno);
X    fclose(seq);
X    return(0);
X}
X
Xint
XSystem(cmd)
Xchar *cmd;
X{
X    long nl;
X    int return_value;
X
X    if ((nl = (long)Open("null:wpl", 1006)) != NULL) {
X	return_value = (Execute(cmd, nl, nl) == -1) ? 0 : -1;
X	Close(nl);
X    } else {
X	ulog(-1, "Unable to open NULL: device, did you mount it?");
X	return_value = -1;
X    }
X    return return_value;
X}
X
X
X/*
X * Set USERMEM to the maximum amount of physical user memory available
X * in bytes.  USERMEM is used to determine the maximum BITS that can be used
X * for compression.
X *
X * SACREDMEM is the amount of physical memory saved for others; compress
X * will hog the rest.
X */
X#ifndef SACREDMEM
X#define SACREDMEM	0
X#endif
X
X#ifndef USERMEM
X/*# ifdef AMIGA
X#   define USERMEM	200000
X# else*/
X#   define USERMEM	450000	/* default user memory */
X/* # endif*/
X#endif
X
X#ifdef interdata		/* (Perkin-Elmer) */
X#define SIGNED_COMPARE_SLOW	/* signed compare is slower than unsigned */
X#endif
X
X#ifdef pdp11
X# define BITS	12	/* max bits/code for 16-bit machine */
X# define NO_UCHAR	/* also if "unsigned char" functions as signed char */
X# undef USERMEM
X#endif /* pdp11 */	/* don't forget to compile with -i */
X
X#ifdef z8000
X# define BITS	12
X# undef vax		/* weird preprocessor */
X# undef USERMEM
X#endif /* z8000 */
X
X#ifdef pcxt
X# define BITS	12
X# undef USERMEM
X#endif /* pcxt */
X
X#ifdef USERMEM
X# if USERMEM >= (433484+SACREDMEM)
X#  define PBITS 16
X# else
X#  if USERMEM >= (229600+SACREDMEM)
X#   define PBITS	15
X#  else
X#   if USERMEM >= (127536+SACREDMEM)
X#    define PBITS	14
X#   else
X#    if USERMEM >= (73464+SACREDMEM)
X#     define PBITS	13
X#    else
X#     define PBITS	12
X#    endif
X#   endif
X#  endif
X# endif
X# undef USERMEM
X#endif /* USERMEM */
X
X#ifdef PBITS		/* Preferred BITS for this memory size */
X# ifndef BITS
X#  define BITS PBITS
X# endif BITS
X#endif /* PBITS */
X
X#if BITS == 16
X# define HSIZE	69001		/* 95% occupancy */
X#endif
X#if BITS == 15
X# define HSIZE	35023		/* 94% occupancy */
X#endif
X#if BITS == 14
X# define HSIZE	18013		/* 91% occupancy */
X#endif
X#if BITS == 13
X# define HSIZE	9001		/* 91% occupancy */
X#endif
X#if BITS <= 12
X# define HSIZE	5003		/* 80% occupancy */
X#endif
X
X#ifdef M_XENIX			/* Stupid compiler can't handle arrays with */
X# if BITS == 16 		/* more than 65535 bytes - so we fake it */
X#  define XENIX_16
X# else
X#  if BITS > 13 		/* Code only handles BITS = 12, 13, or 16 */
X#   define BITS 13
X#  endif
X# endif
X#endif
X
X/*
X * a code_int must be able to hold 2**BITS values of type int, and also -1
X */
X#if BITS > 15
Xtypedef long int	code_int;
X#else
Xtypedef int		code_int;
X#endif
X
X#ifdef SIGNED_COMPARE_SLOW
Xtypedef unsigned long int count_int;
Xtypedef unsigned short int count_short;
X#else
Xtypedef long int	  count_int;
X#endif
X
X#ifdef NO_UCHAR
X typedef char	char_type;
X#else
X typedef	unsigned char	char_type;
X#endif /* UCHAR */
Xchar_type magic_header[] = { "\037\235" };      /* 1F 9D */
X
X/* Defines for third byte of header */
X#define BIT_MASK	0x1f
X#define BLOCK_MASK	0x80
X/* Masks 0x40 and 0x20 are free.  I think 0x20 should mean that there is
X   a fourth header byte (for expansion).
X*/
X#define INIT_BITS 9			/* initial number of bits/code */
X
X#include <stdio.h>
X#include <ctype.h>
X
Xint n_bits;				/* number of bits/code */
Xint maxbits = BITS;			/* user settable max # bits/code */
Xcode_int maxcode;			/* maximum code, given n_bits */
Xcode_int maxmaxcode = 1 << BITS;	/* should NEVER generate this code */
X#define MAXCODE(n_bits)        ((1 << (n_bits)) - 1)
X
Xcount_int htab [HSIZE];
Xunsigned short codetab [HSIZE];
X#define htabof(i)       htab[i]
X#define codetabof(i)    codetab[i]
Xcode_int hsize = HSIZE; 		/* for dynamic table sizing */
Xcount_int fsize;
X
X/*
X * To save much memory, we overlay the table used by compress() with those
X * used by decompress().  The tab_prefix table is the same size and type
X * as the codetab.  The tab_suffix table needs 2**BITS characters.  We
X * get this from the beginning of htab.  The output stack uses the rest
X * of htab, and contains characters.  There is plenty of room for any
X * possible stack (stack used to be 8000 characters).
X */
X
X#define tab_prefixof(i) codetabof(i)
X#define tab_suffixof(i)        (((char_type *)htab)[i])
X#define tab_suffixof1(i)        (((long)htab)+(long)(i))
X#define de_stack	       (char_type *)(tab_suffixof1(1<<BITS))
X
Xcode_int free_ent = 0;			/* first unused entry */
Xint exit_stat = 0;
X
Xcode_int getcode();
X
Xvoid _fprintf() { }
X
Xint nomagic = 0;	/* Use a 3-byte magic number header, unless old file */
X
X/*
X * block compression parameters -- after all codes are used up,
X * and compression rate changes, start over.
X */
Xint block_compress = BLOCK_MASK;
Xint clear_flg = 0;
Xlong int ratio = 0;
X#define CHECK_GAP 10000 /* ratio check interval */
Xcount_int checkpoint = CHECK_GAP;
X/*
X * the next two codes should not be changed lightly, as they must not
X * lie within the contiguous general code space.
X */
X#define FIRST	257	/* first free entry */
X#define CLEAR	256	/* table clear output code */
X
Xint (*bgnd_flag)();
X
Xstatic void decompress ();
Xstatic void compress ();
X
Xvoid
Xuncompress_news()
X{
X    if (freopen(TmpFile, "w", stdout) == NULL) {
X       ulog(-1, "Can't open uncompressed file %s", TmpFile);
X       exit(1);
X    }
X
X    maxbits = BITS;
X    maxmaxcode = 1 << maxbits;
X
X    /* Check the magic number */
X    if (nomagic == 0) {
X       if ((getchar()!=(magic_header[0] & 0xFF))
X	  || (getchar()!=(magic_header[1] & 0xFF))) {
X		ulog(-1, "stdin: not in compressed format");
X		exit(1);
X	}
X	maxbits = getchar();    /* set -b from file */
X	block_compress = maxbits & BLOCK_MASK;
X	maxbits &= BIT_MASK;
X	maxmaxcode = 1 << maxbits;
X	fsize = 100000; 	/* assume stdin large for USERMEM */
X    }
X  decompress();
X
X  fclose(stdout);
X}
X
Xstatic int offset;
Xlong int in_count = 1;			/* length of input */
Xlong int bytes_out;			/* length of compressed output */
Xlong int out_count = 0; 		/* # of codes output (for debugging) */
X
X
X/*
X * Decompress stdin to stdout.	This routine adapts to the codes in the
X * file building the "string" table on-the-fly; requiring no table to
X * be stored in the compressed file.  The tables used herein are shared
X * with those of the compress() routine.  See the definitions above.
X */
X
Xstatic void decompress() {
X    register char_type *stackp;
X    register int finchar;
X    register code_int code, oldcode, incode;
X
X    /*
X     * As above, initialize the first 256 entries in the table.
X     */
X    maxcode = MAXCODE(n_bits = INIT_BITS);
X    for ( code = 255; code >= 0; code-- ) {
X	tab_prefixof(code) = 0;
X	tab_suffixof(code) = (char_type)code;
X    }
X    free_ent = ((block_compress) ? FIRST : 256 );
X
X    finchar = oldcode = getcode();
X    if(oldcode == -1)   /* EOF already? */
X	return; 		/* Get out of here */
X    putchar( (char)finchar );   /* first code must be 8 bits = char */
X    if(ferror(stdout))          /* Crash if can't write */
X	writeerr();
X    stackp = de_stack;
X
X    while ( (code = getcode()) > -1 ) {
X
X	if ( (code == CLEAR) && block_compress ) {
X	    for ( code = 255; code >= 0; code-- )
X		tab_prefixof(code) = 0;
X	    clear_flg = 1;
X	    free_ent = FIRST - 1;
X	    if ( (code = getcode ()) == -1 )    /* O, untimely death! */
X		break;
X	}
X	incode = code;
X	/*
X	 * Special case for KwKwK string.
X	 */
X	if ( code >= free_ent ) {
X	    *stackp++ = finchar;
X	    code = oldcode;
X	}
X
X	/*
X	 * Generate output characters in reverse order
X	 */
X#ifdef SIGNED_COMPARE_SLOW
X	while ( ((unsigned long)code) >= ((unsigned long)256) ) {
X#else
X	while ( code >= 256 ) {
X#endif
X	    *stackp++ = tab_suffixof(code);
X	    code = tab_prefixof(code);
X	}
X	*stackp++ = finchar = tab_suffixof(code);
X
X	/*
X	 * And put them out in forward order
X	 */
X	do
X	    putchar ( *--stackp );
X	while ( stackp > de_stack );
X
X	/*
X	 * Generate the new entry.
X	 */
X	if ( (code=free_ent) < maxmaxcode ) {
X	    tab_prefixof(code) = (unsigned short)oldcode;
X	    tab_suffixof(code) = finchar;
X	    free_ent = code+1;
X	}
X	/*
X	 * Remember previous code.
X	 */
X	oldcode = incode;
X    }
X
X    fflush( stdout );
X    if(ferror(stdout))
X	writeerr();
X}
X
Xchar_type lmask[9] = {0xff, 0xfe, 0xfc, 0xf8, 0xf0, 0xe0, 0xc0, 0x80, 0x00};
Xchar_type rmask[9] = {0x00, 0x01, 0x03, 0x07, 0x0f, 0x1f, 0x3f, 0x7f, 0xff};
X
X/*****************************************************************
X * TAG( getcode )
X *
X * Read one code from the standard input.  If EOF, return -1.
X * Inputs:
X *	stdin
X * Outputs:
X *	code or -1 is returned.
X */
X
Xcode_int
Xgetcode() {
X    /*
X     * On the VAX, it is important to have the register declarations
X     * in exactly the order given, or the asm will break.
X     */
X    register code_int code;
X    static int offset = 0, size = 0;
X    static char_type buf[BITS];
X    register int r_off, bits;
X    register char_type *bp = buf;
X
X    if ( clear_flg > 0 || offset >= size || free_ent > maxcode ) {
X	/*
X	 * If the next entry will be too big for the current code
X	 * size, then we must increase the size.  This implies reading
X	 * a new buffer full, too.
X	 */
X	if ( free_ent > maxcode ) {
X	    n_bits++;
X	    if ( n_bits == maxbits )
X		maxcode = maxmaxcode;	/* won't get any bigger now */
X	    else
X		maxcode = MAXCODE(n_bits);
X	}
X	if ( clear_flg > 0) {
X	    maxcode = MAXCODE (n_bits = INIT_BITS);
X	    clear_flg = 0;
X	}
X	size = fread( buf, 1, n_bits, stdin );
X	if ( size <= 0 )
X	    return -1;			/* end of file */
X	offset = 0;
X	/* Round size down to integral number of codes */
X	size = (size << 3) - (n_bits - 1);
X    }
X    r_off = offset;
X    bits = n_bits;
X	/*
X	 * Get to the first byte.
X	 */
X	bp += (r_off >> 3);
X	r_off &= 7;
X	/* Get first part (low order bits) */
X#ifdef NO_UCHAR
X	code = ((*bp++ >> r_off) & rmask[8 - r_off]) & 0xff;
X#else
X	code = (*bp++ >> r_off);
X#endif /* NO_UCHAR */
X	bits -= (8 - r_off);
X	r_off = 8 - r_off;		/* now, offset into code word */
X	/* Get any 8 bit parts in the middle (<=1 for up to 16 bits). */
X	if ( bits >= 8 ) {
X#ifdef NO_UCHAR
X	    code |= (*bp++ & 0xff) << r_off;
X#else
X	    code |= *bp++ << r_off;
X#endif /* NO_UCHAR */
X	    r_off += 8;
X	    bits -= 8;
X	}
X	/* high order bits. */
X	code |= (*bp & rmask[bits]) << r_off;
X    offset += n_bits;
X
X    return code;
X}
X
Xchar *
Xrindex(s, c)            /* For those who don't have it in libc.a */
Xchar *s, c;
X{
X    char *p;
X    for (p = NULL; *s; s++)
X	if (*s == c)
X	    p = s;
X    return(p);
X}
X
Xvoid
Xwriteerr()
X{
X    perror ( TmpFile );
X    unlink ( TmpFile );
X    exit ( 1 );
X}
X
X/*
X *  assign:path/path/path.../filename
X *
X *  creates path directories
X */
X
Xvoid
XCreateDirsFor(filename)
Xchar *filename;
X{
X    short i;
X    short j;
X
X    for (i = 0; ; ++i) {
X	for (j = i; filename[j] && filename[j] != ':' && filename[j] != '/'; ++j);
X	if (filename[j] == 0)
X	    break;
X	if (filename[j] == ':') {
X	    i = j;
X	    continue;
X	}
X	filename[j] = 0;
X	mkdir(filename);        /*  may fail if parents already exist */
X	filename[j] = '/';
X    }
X}
X
END_OF_FILE
if test 17361 -ne `wc -c <'src/News060/rnews.c'`; then
    echo shar: \"'src/News060/rnews.c'\" unpacked with wrong size!
fi
# end of 'src/News060/rnews.c'
fi
if test -f 'src/getty/getty.c' -a "${1}" != "-c" ; then 
  echo shar: Will not clobber existing file \"'src/getty/getty.c'\"
else
echo shar: Extracting \"'src/getty/getty.c'\" \(19742 characters\)
sed "s/^X//" >'src/getty/getty.c' <<'END_OF_FILE'
X
X/*
X *  GETTY.C
X *
X *  (c)Copyright 1989, Matthew Dillon, All Rights Reserved
X *
X *  Uses:
X *	GETTY:PASSWD
X *	GETTY:LOGFILE
X *	GETTY:Getty-Header
X *
X *  GETTY   <options>	<modem-commands>
X *
X *	-Sdevicenam Tells GETTY which serial.device to use, default
X *		    serial.device
X *
X *	-Uunitnum   Tells GETTY which unit number to use, default 0
X *
X *	-A	    Always talk to the modem at the first baud
X *		    rate in the -B list and ignore the baud rate
X *		    in the CONNECT message.
X *
X *	-7	    use SERF_7WIRE while online.
X *
X *	-Bn	    Set baud rate.  If specified multiple times the
X *		    first one is the default and remaining ones
X *		    switched to when a BREAK is received after a
X *		    connect.  Also determines how CONNECT messages
X *		    are interpreted.  A CONNECT with no number uses
X *		    the first -B entry while CONNECTs with numbers
X *		    use those numbers regardless of any -B entries.
X *
X *	-Mc	    Set modem type:
X *			c = m	= multimodem
X *			    h	= hays
X *			    d	= dumb (no AT or +++ cmds are ever sent),
X *				  normally used with only one specified
X *				  baud rate.
X *
X *	-m1	    Turn on the modem speaker during dialing/connect
X *		    (default is -m0)
X *
X *	-h0	    Ignore CD (carrier detect), default is to use
X *		    carrier detect.
X *
X *	-c0	    Ignore CONNECT message (i.e. you are not connected
X *		    to a modem, usually combined with -h0).  Default is
X *		    to expect a CONNECT message.
X *
X *	-d0	    do not use DTR to drop connection.	Default is
X *		    to drop DTR to drop a connection.  If this option
X *		    is chosen the +++ and ATH0 sequence will be used
X *		    to drop a connection.
X *
X *	-xn	    Set debug level.  Also causes log output to go
X *		    to stdout instead of GETTY:LOGFILE
X *
X *	-0	    QUIT - Kills any running getty for the specified
X *		    port.
X *
X *	Any fields specified without a '-' are considered modem commands
X *	used to initialize/reinitialize the modem.  Up to 16 fields may
X *	be specified (each is sent to the modem in 1 second intervals)
X */
X
X#include <exec/types.h>
X#include <exec/lists.h>
X#include <exec/devices.h>
X#include <devices/timer.h>
X#include <devices/serial.h>
X#include <libraries/dos.h>
X#include <libraries/dosextens.h>
X#include <hardware/cia.h>
X#include <stdio.h>
X#include <stdlib.h>
X#include <proto/all.h>
X#include <pwd.h>
X#include "/version.h"
X
X#include "log.h"
X
XIDENT(".01");
X
X#define arysize(ary)    (sizeof(ary)/sizeof((ary)[0]))
X
X#define ST_WAITCD	0
X#define ST_CONNECT	1
X#define ST_LOGIN	2
X#define ST_PASSWD	3
X#define ST_RUN		4
X
Xtypedef struct IORequest    IOR;
Xtypedef struct timerequest  IOT;
Xtypedef struct IOExtSer     IOSER;
Xtypedef struct MsgPort	    PORT;
Xtypedef struct List	    LIST;
Xtypedef struct Node	    NODE;
Xtypedef struct Message	    MSG;
X
Xtypedef struct GMsg {
X    struct Message  Msg;
X    short   Cmd;
X    long    Data1;
X    void    *Data2;
X} GMsg;
X
Xextern struct ProcID *RunPasswdEntry();
X
Xchar	*CopyRight = "(c)Copyright 1989, Matthew Dillon, All Rights Reserved\r\n";
Xchar	*ComPortName;
X
Xchar	*DeviceName = "serial.device";
Xlong	DeviceUnit  = 0;
Xlong	NullFH;
Xchar	SpeakerLevel	= 0;
Xchar	AnswerRing	= 2;	/*  default, answer on second ring */
Xchar	SpeakerOpt	= 0;
Xchar	IgnoreCD	= 0;
Xchar	IgnoreConnect	= 0;
Xchar	IgnoreDTR	= 0;
Xchar	BaudAdjust	= 0;
Xchar	DropOnExit	= 1;
Xchar	ModemType	= 'h';
Xchar	ZeroOption	= 0;
Xchar	Wire7		= 0;		/*  use 7 wire while online */
Xchar	Locked		= 0;		/*  serial port is lcked    */
Xlong	Bauds[16] = { 9600 };		/*  up 16 baud rates	    */
Xchar	*AtFields[16];
X
XPORT	*ComPort;
XPORT	*IoSink;	/*  Sink for IO reqs.	*/
Xlong	IoSinkMask;
Xlong	ComPortMask;
X
XIOT	Iot;		/*  check-carrier	*/
XIOSER	Iosr;		/*  serial read-req	*/
XIOSER	Iosw;		/*  serial write-req	*/
X
Xchar	IotIP;		/*  Iot0 in progress	*/
Xchar	IosrIP;
X
Xchar	ScrBuf[256];
Xchar	ConnectBuf[64];
Xchar	LoginBuf[32];
Xchar	PasswdBuf[32];
Xchar	RxBuf[32];
Xchar	HaveConnectMsg;
Xchar	HaveLogin;
Xchar	HavePasswd;
X
Xshort	State;
Xshort	Index;
Xshort	BIndex;
X
Xshort CountDown;
Xshort GotOffPort;
X
Xvoid	SerPuts();
Xvoid	Set7Wire();
Xvoid	xexit();
Xvoid	Disconnect();
Xvoid	ClearRecv();
Xvoid	InitModem();
Xvoid	SetParams();
Xvoid	DoOptions();
Xvoid	SerialOff();
X
XCXBRK()
X{
X    return(0);
X}
X
Xmain(ac, av)
Xchar *av[];
X{
X    extern int IAmGetty;
X
X    IAmGetty = 1;	/*  for LockSerialPort()/UnLockSerialPort()   */
X
X    puts(CopyRight);
X    fflush(stdout);
X
X    LogProgram = "Getty";
X    LogWho = LoginBuf;
X    LogFile = "Getty:LOGFILE";
X    PasswdFile = "Getty:Passwd";
X
X    DoOptions(ac, av);
X
X    IoSink = CreatePort(NULL, 0);
X    IoSinkMask = 1 << IoSink->mp_SigBit;
X
X    ComPortName = malloc(strlen(DeviceName) + 20);
X    sprintf(ComPortName, "Getty.%s.%d", DeviceName, DeviceUnit);
X
X    Forbid();
X    if (ComPort = FindPort(ComPortName)) {
X	GMsg msg;
X	msg.Cmd = 'O';
X	msg.Data1 = ac;
X	msg.Data2 = (void *)av;
X	msg.Msg.mn_ReplyPort = IoSink;
X	PutMsg(ComPort, &msg.Msg);
X	WaitPort(IoSink);
X	Remove(&msg);
X	Permit();
X	puts("Options updated");
X	xexit(0);
X    }
X    ComPort = CreatePort(ComPortName, 0);
X    Permit();
X
X    ComPortMask = 1 << ComPort->mp_SigBit;
X
X    NullFH = Open("NULL:", 1006);
X    if (NullFH == NULL) {
X	ulog(-1, "GETTY REQUIRES NULL: HANDLER!");
X	puts("Requires NULL: handler!");
X	xexit(1);
X    }
X    if (LogToStdout == 0) {
X	freopen("NULL:", "r", stdin);
X	freopen("NULL:", "w", stdout);
X	freopen("NULL:", "w", stderr);
X    }
X
X
X    /*
X     *	Timer Device
X     */
X
X    Iot.tr_node.io_Message.mn_ReplyPort = IoSink;
X    if (OpenDevice("timer.device", UNIT_VBLANK, &Iot, 0)) {
X	Iot.tr_node.io_Device = NULL;
X	xexit(20);
X    }
X    Iot.tr_node.io_Command = TR_ADDREQUEST;
X
X    /*
X     *	SERIAL.DEVICE
X     */
X
X    Iosr.IOSer.io_Message.mn_ReplyPort = IoSink;
X    Iosr.io_SerFlags = SERF_XDISABLED | SERF_7WIRE | SERF_RAD_BOOGIE | SERF_SHARED;
X    if (OpenDevice(DeviceName, DeviceUnit, &Iosr, 0)) {
X	Iosr.IOSer.io_Device = NULL;
X	xexit(21);
X    }
X
X    Iosw = Iosr;
X
X    Iosw.io_SerFlags = SERF_XDISABLED | SERF_RAD_BOOGIE | SERF_SHARED;
X    Iosw.io_ExtFlags = 0;
X    Iosw.io_Baud = Bauds[0];
X    Iosw.io_ReadLen = 8;
X    Iosw.io_WriteLen = 8;
X    Iosw.io_StopBits = 1;
X
X    SetParams(&Iosw);
X
X    /*
X     *	Run Operation
X     */
X
X    State = ST_WAITCD;
X    Iot.tr_time.tv_secs = 1;
X    Iot.tr_time.tv_micro= 0;
X    IotIP = 1;
X    SendIO(&Iot);
X
X    InitModem();
X
X    Signal(FindTask(NULL), IoSinkMask);
X
X    for (;;) {
X	long mask;
X	IOR  *ior;
X	GMsg *msg;
X	mask = Wait(SIGBREAKF_CTRL_C | IoSinkMask | ComPortMask);
X
X	ulog(2, "State = %d", State);
X
X	if (mask & SIGBREAKF_CTRL_C)
X	    break;
X
X	if (msg = (GMsg *)GetMsg(ComPort)) {
X	    do {
X		switch(msg->Cmd) {
X		case 'O':
X		    DoOptions(msg->Data1, msg->Data2);
X		    break;
X		}
X		ReplyMsg((MSG *)msg);
X		if (ZeroOption)
X		    xexit(0);
X	    } while (msg = (GMsg *)GetMsg(ComPort));
X	    if (State == ST_WAITCD && !GotOffPort)
X		Disconnect(0);
X	}
X
X	while (ior = (IOR *)GetMsg(IoSink)) {
X	    if (ior == (IOR *)&Iot) {
X		short diu;
X
X		IotIP = 0;
X		Iot.tr_time.tv_secs = 1;
X		Iot.tr_time.tv_micro= 0;
X
X		/*
X		 *  If OpenCnt > 1, disable all processing.
X		 *
X		 *  When returns to <= 1 reset serial port.
X		 */
X
X		diu = DeviceInUse();
X		if (diu) {
X		    if (GotOffPort == 0) {
X			ulog(-1, "Device in use");
X			if (Locked)
X			    ulog(-1, "getting off port");
X			SerialOff();
X			GotOffPort = 1;
X			State = ST_WAITCD;
X		    }
X		    SendIO(&Iot);
X		    IotIP = 1;
X		    continue;
X		}
X		if (GotOffPort) {
X		    if (Locked == 0) {
X			LockSerialPort(DeviceName, DeviceUnit);
X			Locked = 1;
X		    }
X		    GotOffPort = 0;
X		    ulog(-1, "Getty resetting");
X		    SendIO(&Iot);
X		    IotIP = 1;
X		    Disconnect(1);
X		    continue;
X		}
X		if (State == ST_WAITCD)
X		    SerialOff();
X
X		if (State != ST_WAITCD && IosrIP == 0) {
X		    if (Locked == 0)
X			LockSerialPort(DeviceName, DeviceUnit);
X		    Locked = 1;
X		    ulog(-1, "Serial Port Locked");
X
X		    Iosr.IOSer.io_Command = CMD_READ;
X		    Iosr.IOSer.io_Data = (APTR)RxBuf;
X		    Iosr.IOSer.io_Length = 1;
X		    SendIO(&Iosr);
X		    IosrIP = 1;
X		    ulog(1, "Carrier, Getty getting on port");
X		}
X
X		Iosw.IOSer.io_Command = SDCMD_QUERY;
X		DoIO(&Iosw);
X		if (IgnoreCD)
X		    Iosw.io_Status &= ~CIAF_COMCD;
X
X		/*
X		 *  If state connected and we loose carrier, disconnect.
X		 *
X		 *  If state connected and timeout occurs disconnect.
X		 */
X
X		if (State != ST_WAITCD) {
X		    if ((Iosw.io_Status & CIAF_COMCD) != 0) {
X			ulog(1, "Getty, Carrier lost");
X			Disconnect(0);
X		    } else {
X			if (--CountDown == 0) {
X			    ulog(1, "Getty, Timeout, Disconnecting");
X			    Disconnect(1);
X			    Iosw.io_Status |= CIAF_COMCD; /* for below */
X			}
X		    }
X		}
X
X		switch(State) {
X		case ST_WAITCD:
X		    if ((Iosw.io_Status & CIAF_COMCD) == 0) {
X			State = ST_CONNECT;
X			CountDown = 60;     /*	allow 60 seconds */
X			ulog(-1, "Carrier Detect");
X		    } else {
X			Iot.tr_time.tv_secs = 2;
X		    }
X		    break;
X		case ST_CONNECT:
X		    /*
X		     *	Wait for CONNECT message, then send Login:
X		     */
X
X		    if (IgnoreConnect && HaveConnectMsg == 0) {
X			if (Wire7)
X			    Set7Wire(&Iosw);
X			HaveConnectMsg = 1;
X			ulog(-1, "Connect");
X		    }
X
X		    if (HaveConnectMsg) {
X			FILE *fi;
X
X			Delay(50);
X			ClearRecv();
X			if (fi = fopen("Getty:Getty-Header", "r")) {
X			    while (fgets(ScrBuf, sizeof(ScrBuf), fi)) {
X				SerPuts(ScrBuf);
X				SerPuts("\r");
X			    }
X			    fclose(fi);
X			}
X			SerPuts("Login: ");
X			ulog(-1, "Getty, Connect, Login");
X			State = ST_LOGIN;
X			Index = 0;
X			HaveLogin = 0;
X			LoginBuf[0] = 0;
X		    }
X		    break;
X		case ST_LOGIN:	    /*	wait Login: response	*/
X		    if (HaveLogin) {
X			if (LoginBuf[0] == 0) {
X			    State = ST_CONNECT;
X			    break;
X			}
X			ClearRecv();
X			PasswdBuf[0] = 0;
X
X			/*
X			 *  If no password required, else request
X			 *  password.
X			 */
X
X			if (CheckLoginAndPassword()) {
X			    HavePasswd = 1;
X			    Index = 0;
X			    State = ST_PASSWD;
X			} else {
X			    SerPuts("Password: ");
X			    ulog(1, "Getty, Passwd");
X			    State = ST_PASSWD;
X			    HavePasswd = 0;
X			    Index = 0;
X			}
X		    }
X		    break;
X		case ST_PASSWD:     /*	wait Password: response */
X		    if (HavePasswd) {
X			if (CheckLoginAndPassword()) {
X			    ulog(-1, "login %s", LoginBuf);
X
X			    /*
X			     *	Disable read requests but leave serial
X			     *	port locked.
X			     */
X
X			    if (IosrIP) {
X				AbortIO(&Iosr);
X				WaitIO(&Iosr);
X				IosrIP = 0;
X			    }
X
X			    /*
X			     *	If run successful, leave read req and
X			     *	timer disabled.
X			     */
X
X			    RunPasswdEntry();
X
X			    if (DropOnExit)
X				Disconnect(1);
X			    else
X				State = ST_CONNECT;
X			    ulog(-1, "disconnect");
X			} else {
X			    SerPuts("Login Failed.\r\n\n");
X			    State = ST_CONNECT;
X			    ulog(-1, "LoginFailed user=%s pass=%s", LoginBuf, PasswdBuf);
X			}
X			HaveLogin = 0;
X			HavePasswd= 0;
X			LoginBuf[0] = 0;
X		    }
X		    break;
X		}
X
X		/*
X		 *  Make no read requests while running 3rd party
X		 *  program, else resend read request.
X		 */
X
X		if (IotIP == 0) {
X		    IotIP = 1;
X		    SendIO(&Iot);
X		}
X	    }
X
X	    /*
X	     *	RECEIVED SERIAL READ DATA
X	     */
X
X	    if (ior == (IOR *)&Iosr) {
X		IosrIP = 0;
X
X		Iosw.IOSer.io_Command = SDCMD_QUERY;
X		DoIO(&Iosw);
X		if (IgnoreCD)
X		    Iosw.io_Status &= ~CIAF_COMCD;
X
X		/*
X		 *  BREAK used to switch baud rates between allowed
X		 *  values
X		 */
X
X		if (Iosw.io_Status & IO_STATF_READBREAK) {
X		    if (BaudAdjust == 0 && (State == ST_LOGIN || State == ST_PASSWD)) {
X			if (++BIndex == arysize(Bauds))
X			    BIndex = 0;
X			if (Bauds[BIndex] == 0)
X			    BIndex = 0;
X			Iosw.io_Baud = Bauds[BIndex];
X			SetParams(&Iosw);
X			if (Wire7)
X			    Set7Wire(&Iosw);
X			ulog(1, "<BREAK> to %d baud", Iosw.io_Baud);
X			Delay(100);
X			ClearRecv();
X			Index = 0;
X			State = ST_CONNECT;
X		    }
X		} else
X		if (Iosr.IOSer.io_Actual == 1) {
X		    char *ptr;
X		    UBYTE c = (UBYTE)RxBuf[0];
X
X		    ulog(2, "Rx %02x %c", c, (c < 32) ? ' ' : c);
X		    c &= 0x7F;
X
X		    switch(State) {
X		    case ST_WAITCD:	/*  looking for CONNECT msg */
X		    case ST_CONNECT:	/*  looking for CONNECT msg */
X			ptr = ConnectBuf;
X			break;
X		    case ST_LOGIN:	/*  looking for login name  */
X			ptr = LoginBuf;
X			break;
X		    case ST_PASSWD:	/*  looking for password    */
X			ptr = PasswdBuf;
X			break;
X		    }
X		    if (State == ST_LOGIN && HaveLogin)
X			c = 0;
X		    if (State == ST_PASSWD && HavePasswd)
X			c = 0;
X
X		    switch(c) {
X		    case 0:
X			break;
X		    case 8:
X			if (State == ST_LOGIN && HaveLogin)
X			    break;
X			if (Index) {
X			    if (State == ST_LOGIN)
X				SerPuts("\010 \010");
X			    --Index;
X			}
X			break;
X		    case 10:
X		    case 13:
X			ptr[Index] = 0;
X			Index = 0;
X			switch(State) {
X			case ST_WAITCD:
X			case ST_CONNECT:
X			    if (strncmp(ptr, "CONNECT", 7)) {
X				ulog(1, "Looking for CONNECT, got '%s'", ptr);
X				break;
X			    }
X			    Delay(50);
X			    HaveConnectMsg = 1;
X			    if (BaudAdjust) {
X				ulog(-1, "Connect Auto-Baud %d", Iosw.io_Baud);
X			    } else {
X				char *str = ptr + 7;
X				while (*str && (*str == 9 || *str == ' '))
X				    ++str;
X				if (*str >= '0' && *str <= '9')
X				    Iosw.io_Baud = atoi(str);
X				else
X				    Iosw.io_Baud = Bauds[0];
X				SetParams(&Iosw);
X				ulog(-1, "Connect at %d baud", Iosw.io_Baud);
X			    }
X			    if (Wire7)
X				Set7Wire(&Iosw);
X			    break;
X			case ST_LOGIN:
X			    HaveLogin = 1;
X			    SerPuts("\r\n");
X			    ulog(1, "Login: %s", ptr);
X			    break;
X			case ST_PASSWD:
X			    HavePasswd = 1;
X			    SerPuts("\r\n");
X			    ulog(1, "Password: %s", ptr);
X			    break;
X			}
X			break;
X		    default:
X			if (Index == 31)
X			    break;
X			if (State == ST_LOGIN) {
X			    char cc[2];
X			    cc[0] = c;
X			    cc[1] = 0;
X			    SerPuts(cc);
X			}
X			ptr[Index++] = c;
X			break;
X		    }
X		}
X		if (IosrIP == 0) {
X		    Iosr.IOSer.io_Command = CMD_READ;
X		    Iosr.IOSer.io_Data = (APTR)RxBuf;
X		    Iosr.IOSer.io_Length = 1;
X		    SendIO(&Iosr);
X		    IosrIP = 1;
X		}
X	    }
X	}
X    }
X    xexit(0);
X}
X
Xvoid
XSerialOff()
X{
X    if (IosrIP) {
X	AbortIO(&Iosr);
X	WaitIO(&Iosr);
X	IosrIP = 0;
X    }
X    if (Locked) {
X	UnLockSerialPort(DeviceName, DeviceUnit);
X	Locked = 0;
X	ulog(1, "Serial Port UnLocked");
X    }
X}
X
Xvoid
Xxexit(code)
X{
X    if (ComPortMask) {
X	GMsg *msg;
X	Forbid();
X	while (msg = (GMsg *)GetMsg(ComPort))
X	    ReplyMsg((MSG *)msg);
X	DeletePort(ComPort);
X	Permit();
X    }
X    if (IotIP) {
X	AbortIO(&Iot);
X	WaitIO(&Iot);
X    }
X    if (Iot.tr_node.io_Device)
X	CloseDevice(&Iot);
X
X    if (IosrIP) {
X	AbortIO(&Iosr);
X	WaitIO(&Iosr);
X    }
X    if (Iosr.IOSer.io_Device)
X	CloseDevice(&Iosr);
X
X    if (IoSink)
X	DeletePort(IoSink);
X
X    if (NullFH)
X	Close(NullFH);
X
X    if (Locked)
X	UnLockSerialPort(DeviceName, DeviceUnit);
X
X    exit(code);
X}
X
Xvoid
XSerPuts(str)
Xchar *str;
X{
X    Iosw.IOSer.io_Command = CMD_WRITE;
X    Iosw.IOSer.io_Data = (APTR)str;
X    Iosw.IOSer.io_Length = strlen(str);
X    DoIO(&Iosw);
X}
X
Xstatic short RxDisableIP;
X
Xvoid
XRxDisable()
X{
X    RxDisableIP = IosrIP;
X    if (IosrIP) {
X	AbortIO(&Iosr);
X	WaitIO(&Iosr);
X	IosrIP = 0;
X    }
X}
X
Xvoid
XRxEnable()
X{
X    if (RxDisableIP && IosrIP == 0) {
X	Iosr.IOSer.io_Command = CMD_READ;
X	Iosr.IOSer.io_Data = (APTR)RxBuf;
X	Iosr.IOSer.io_Length = 1;
X	SendIO(&Iosr);
X	IosrIP = 1;
X    }
X}
X
Xvoid
XClearRecv()
X{
X    RxDisable();
X    ulog(1, "Clear beg");
X    for (;;) {
X	Iosr.IOSer.io_Command = SDCMD_QUERY;
X	DoIO(&Iosr);
X	if ((Iosr.IOSer.io_Length = Iosr.IOSer.io_Actual) <= 0)
X	    break;
X	if (Iosr.IOSer.io_Length > sizeof(RxBuf))
X	    Iosr.IOSer.io_Length = sizeof(RxBuf);
X	Iosr.IOSer.io_Data = (APTR)RxBuf;
X	Iosr.IOSer.io_Command = CMD_READ;
X	DoIO(&Iosr);
X    }
X    ulog(1, "Clear end");
X    RxEnable();
X}
X
Xvoid
XSetParams(ior)
XIOSER *ior;
X{
X    int error;
X
X    if (IosrIP)
X	AbortIO(&Iosr);
X    ior->IOSer.io_Command = SDCMD_SETPARAMS;
X    error = DoIO(ior);
X    if (error)
X	printf("SetParams, error %d\n", error);
X}
X
Xvoid
XSet7Wire(ior)
XIOSER *ior;
X{
X    short error;
X
X    if (IosrIP)
X	AbortIO(&Iosr);
X    ior->IOSer.io_Command = SDCMD_SETPARAMS;
X    ior->io_SerFlags |= SERF_7WIRE;
X    error = DoIO(ior);
X    ior->io_SerFlags &= ~SERF_7WIRE;
X    if (error)
X	printf("SetParams, error %d\n", error);
X}
X
Xvoid
XDisconnect(dropdtr)
X{
X    short retry = (IgnoreDTR) ? 2 : 10;
X
X    ulog(1, "Disconnect drop=%d", dropdtr);
X    HaveConnectMsg = 0;
X    HaveLogin	= 0;
X    HavePasswd	= 0;
X    LoginBuf[0] = 0;
X    PasswdBuf[0] = 0;
X    Index = 0;
X    State = ST_WAITCD;
X
X    while (dropdtr && DeviceInUse() == 0) {
X	short i;
X
X	RxDisable();
X
X	if (IgnoreDTR) {
X	    if (ModemType != 'd') {
X		Delay(70);
X		SerPuts("+++");
X		Delay(70);
X		SerPuts("\010\010\r");
X		Delay(10);
X		SerPuts("ATH0\r");
X		Delay(120);
X	    }
X	} else {
X	    CloseDevice(&Iosr);
X	    Iosr.IOSer.io_Device = NULL;    /*	so xexit doesn't reclose */
X	    for (i = 0; i < 5; ++i) {       /*  5 seconds   */
X		Delay(50);
X		if (SetSignal(SIGBREAKF_CTRL_C, 0) & SIGBREAKF_CTRL_C)
X		    xexit(23);
X	    }
X
X	   /*
X	    *  Use Iosr to re-open serial device so we don't loose
X	    *  our config.
X	    */
X
X	    if (OpenDevice(DeviceName, DeviceUnit, &Iosr, 0)) {
X		Iosr.IOSer.io_Device = NULL;
X		xexit(22);
X	    }
X	    Iosw.IOSer.io_Device = Iosr.IOSer.io_Device;
X	    Iosw.IOSer.io_Unit = Iosr.IOSer.io_Unit;
X	    SetParams(&Iosw);
X	}
X
X	/*
X	 *  Loop until carrier lost
X	 */
X
X	Iosw.IOSer.io_Command = SDCMD_QUERY;
X	DoIO(&Iosw);
X	if (IgnoreCD)
X	    Iosw.io_Status &= ~CIAF_COMCD;
X
X	RxEnable();
X
X	if ((Iosw.io_Status & CIAF_COMCD) != 0)
X	    break;
X	if (--retry == 0) {
X	    if (IgnoreDTR == 0)
X		puts("Getty: unable to disconnect!");
X	    break;
X	}
X    }
X    if (DeviceInUse() == 0)
X	InitModem();
X}
X
Xvoid
XInitModem()
X{
X    char buf[64];
X    short i;
X
X    RxDisable();
X    ulog(1, "Init Modem");
X    Iosw.io_Baud = Bauds[0];	/*  reset baud rate */
X    BIndex = 0;
X    SetParams(&Iosw);
X    RxEnable();
X
X    switch(ModemType) {
X    case 'm':               /*  Multi Modem     */
X	SerPuts("\010\010\r");
X	Delay(10);
X	SerPuts("AT\r");
X	Delay(50);
X	sprintf(buf, "ATM%dS0=%dX4$BA%d&E%d\r",
X	    SpeakerLevel,
X	    AnswerRing,
X	    !BaudAdjust,
X	    (Wire7) ? 4 : 3
X	);
X	SerPuts(buf);
X	break;
X    case 'h':
X	SerPuts("\010\010\r");
X	Delay(10);
X	SerPuts("ATZ\r");
X	Delay(120);
X	strcpy(buf, "AT");
X	if (SpeakerOpt)
X	    sprintf(buf + strlen(buf), "M%d", SpeakerLevel);
X	sprintf(buf + strlen(buf), "S0=%d", AnswerRing);
X	strcat(buf, "\r");
X	SerPuts(buf);
X	break;
X    case 'd':
X	SerPuts("\010\010\r");
X	break;
X    }
X    for (i = 0; i < arysize(AtFields) && AtFields[i]; ++i) {
X	Delay(50);
X	SerPuts(AtFields[i]);
X	SerPuts("\r");
X    }
X    Delay(20);
X    ClearRecv();
X    Index = 0;
X}
X
Xvoid
XDoOptions(ac, av)
Xchar *av[];
X{
X    short i;
X    short bi = 0;
X    short fi = 0;
X    long v;
X
X    for (i = 1; i < ac; ++i) {
X	char *ptr = av[i];
X	if (*ptr != '-') {
X	    if (fi != arysize(AtFields))
X		AtFields[fi++] = ptr;
X	    else
X		puts("AT field overflow");
X	    continue;
X	}
X	if (*++ptr)             /*  skip -      */
X	    ++ptr;		/*  and option	*/
X	v = atoi(ptr);
X	switch(ptr[-1]) {
X	case '0':
X	    ZeroOption = 1;
X	    break;
X	case '7':
X	    Wire7 = 1;
X	    break;
X	case 'S':
X	    DeviceName = ptr;
X	    break;
X	case 'U':
X	    DeviceUnit = v;
X	    break;
X	case 'M':
X	    ModemType = *ptr;
X	    break;
X	case 'A':
X	    BaudAdjust = 1;
X	    break;
X	case 'B':
X	    if (bi != arysize(Bauds))
X		Bauds[bi++] = v;
X	    else
X		puts("-B field overflow");
X	    break;
X	case 'm':
X	    SpeakerOpt = 1;
X	    SpeakerLevel = v;
X	    break;
X	case 'r':
X	    AnswerRing = v;
X	    break;
X	case 'h':
X	    IgnoreCD = !v;
X	    break;
X	case 'c':
X	    IgnoreConnect = !v;
X	    break;
X	case 'd':
X	    IgnoreDTR = !v;
X	    break;
X	case 'x':
X	    LogLevel = v;
X	    LogToStdout = (v >= 0);
X	    break;
X	default:
X	    printf("Warning, Bad option: -%s\n", ptr);
X	    break;
X	}
X    }
X    if (fi && fi != arysize(AtFields))
X	AtFields[fi] = NULL;
X    if (bi && bi != arysize(Bauds))
X	Bauds[bi] = 0;
X}
X
XDeviceInUse()
X{
X    return(Iosr.IOSer.io_Device->dd_Library.lib_OpenCnt > 1);
X}
X
END_OF_FILE
if test 19742 -ne `wc -c <'src/getty/getty.c'`; then
    echo shar: \"'src/getty/getty.c'\" unpacked with wrong size!
fi
# end of 'src/getty/getty.c'
fi
echo shar: End of archive 9 \(of 16\).
cp /dev/null ark9isdone
MISSING=""
for I in 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 ; do
    if test ! -f ark${I}isdone ; then
	MISSING="${MISSING} ${I}"
    fi
done
if test "${MISSING}" = "" ; then
    echo You have unpacked all 16 archives.
    rm -f ark[1-9]isdone ark[1-9][0-9]isdone
else
    echo You still need to unpack the following archives:
    echo "        " ${MISSING}
fi
##  End of shell archive.
exit 0
-- 
Submissions to comp.sources.amiga and comp.binaries.amiga should be sent to:
	amiga@cs.odu.edu	
or	amiga@xanth.cs.odu.edu	( obsolescent mailers may need this address )
or	...!uunet!xanth!amiga	( very obsolescent mailers need this address )

Comments, questions, and suggestions should be addressed to ``amiga-request''
(please only use ``amiga'' for actual submissions) at the above addresses.