Frank da Cruz
As of:
C-Kermit 9.0.300, 30 June 2011
Last update:
Sun Nov 13 07:03:19 2022
IF YOU ARE READING A PLAIN-TEXT version of this document, note that this
file is a plain-text dump of a Web page. You can visit the original (and
possibly more up-to-date) Web page here:
http://www.kermitproject.org/ckcplm.html
1. INTRODUCTION
2. FILES
3. SOURCE CODE PORTABILITY AND STYLE
4. MODULES
4.A. Group A: Library Routines
4.B. Group B: Kermit File Transfer
4.C. Group C: Character-Set Conversion
4.D. Group D: User Interface
4.E. Group E: Platform-Dependent I/O
4.F. Group F: Network Support
4.G. Group G: Formatted Screen Support
4.H. Group H: Pseudoterminal Support
4.I. Group I: Security
I. APPENDIX I: FILE PERMISSIONS
The Kermit Protocol is specified in the book
Kermit, A File Transfer Protocol by Frank da Cruz, Digital Press /
Butterworth Heinemann, Newton, MA, USA (1987), 379 pages, ISBN
0-932376-88-6. It is assumed the reader is familiar with the Kermit
protocol specification.
This file describes the relationship among the modules and functions of
C-Kermit 5A and later, and other programming considerations. C-Kermit is
designed to be portable to any kind of computer that has a C compiler. The
source code is broken into many files that are grouped according to their
function, as shown in the Contents.
C-Kermit has seen constant development since 1985. Throughout its history,
there has been a neverending tug-of-war among:
- Functionality: adding new features, fixing bugs, improving performance.
- Adding support for new platforms or communication methods.
- "Buzzword 1.0 compliance".
The latter category is the most frustrating, since it generally involves
massive changes just to keep the software doing what it did before in some
new setting: e.g. the K&R-to-ANSIC conversion (which had to be done, of
course, without breaking K&R); Y2K (not a big deal in our case); the many
and varied UNIX and other API "standards" with which to "comply".
Upon first glance at the source code, you will probably be appalled.
Many will be tempted to clean it up and modernize it. But as soon as you
do, you are sure to break something. Remember that above all else, the
C-Kermit code is portable to every Unix platform that ever existed,
going back Unix V7 (1979)*, and to several other completely different and
unrelated operating-system families such as DEC/HP VMS, DG AOS/VS, and
Stratus VOS, as well as to some Unix offshoots like OS-9 and Plan 9
(from Outer Space). Every release of Kermit has been checked on every
platform available — the older the better! — to make sure it
still builds and runs. Even today (2011), there are modern Unix systems
that have non-ANSI C compilers, foremost among them HP-UX (where an
ANSI optimizing C compiler is available, but only as an expensive add-on).
In a way, portability is the most important feature of C-Kermit and every
effort should be made to preserve it through future releases.
Voluminous edit histories are available going back to May 1985. The
first versions of C-Kermit were done on our DEC
VAX-11/750 with Ultrix 1.0 and 2.0 (as well as departmental 750s with
4.2BSD**), DEC Pro-380 workstations (desktop PDP-11s) running 2.9BSD, which
was ported
to the 380 by us. Later (1988 or so) on a big VAX 8650 with Ultrix,
which became an 8700 (these no doubt weighed several tons), and finally a
succession of non-DEC equipment: an Encore Multimax, 25 years worth of Suns,
and now Linux on HP Blades. We also had
our own VMS development systems for some years. All this plus a generous
assortment of departmental and offsite guest accounts on a multitude of
platforms. Anyway, the edit histories:
_________________________________
*
| C-Kermit 6.0 was the last one to be built on V7, as I recall. The code
should still be good for V7 but it probably has outgrown the 16-bit address
space. In any case there is still a V7 makefile target and a V7 path
through the forest of #ifdefs in the code if anybody is running V7 on an
emulator and would like to try building C-Kermit. There is no support for
V6 but that is only because no V6 system was ever found for development.
Notice that some other 16-bit Unixes are supported in the code, including
2.9BSD and Tandy Xenix 3.0, but have not been tried since C-Kermit 6.0
|
**
| C-Kermit 9.0.300 was built successfully on 4.2BSD about 25
years later, in June 2011.
|
[ Contents ]
[ C-Kermit ]
[ Kermit Home ]
C-Kermit source files begin with the two letters "
ck", for example
ckutio.c. Filenames are kept short (6.3) for maximum portability and
(obviously I hope) do not contain spaces or more than one period. The third
character in the name denotes something about the function group and the
expected level of portability:
a
| General descriptive material and documentation (text)
|
b
| BOO file encoders and decoders (obsolete)
|
c
| All platforms with C compilers (*)
|
d
| Data General AOS/VS
|
e
| Reserved for "ckermit" files, like ckermit.ini,
ckermit2.txt
|
f
| (reserved)
|
g
| (reserved)
|
h
| (reserved)
|
i
| Commodore Amiga (Intuition)
|
j
| (unused)
|
k
| (unused)
|
l
| Stratus VOS
|
m
| Macintosh with Mac OS 1-9
|
n
| (unused)
|
o
| OS/2 and Microsoft Windows 9x/ME/NT/2000/XP/Vista/etc
|
p
| Plan 9 from Bell Labs
|
q
| (reserved)
|
r
| DEC PDP-11 with RSTS/E (never used, open for reassignment)
|
s
| Atari ST GEMDOS (last supported in version 5A(189))
|
t
| DEC PDP-11 with RT-11 (never used, open for reassignment)
|
u
| Unix-based operating systems (*)
|
v
| VMS and OpenVMS
|
w
| Wart (Lex-like preprocessor, platform independent)
|
x
| (reserved)
|
y
| (reserved)
|
z
| (reserved)
|
0-3
| (reserved)
|
4
| IBM AS/400
|
5-8
| (reserved)
|
9
| Microware OS-9
|
_
| (underscore) Encryption modules
|
(*) In fact there is little distinction between the ckc*.* and
cku*.* categories. It would make more sense for all cku*.*
modules to be ckc*.* ones, except ckufio.c,
ckutio.c, ckucon.c, ckucns.c, and
ckupty.c, which truly are specific to Unix. The rest
(ckuus*.c, ckucmd.c, etc) are quite portable.
One hint before proceeding: functions are scattered all over the
ckc*.c and cku*.c modules, where module size has begun to
take precedence over the desirability of grouping related functions together,
the aim being to keep any particular module from growing disproportionately
large. The easiest way (in UNIX) to find out in what source file a given
function is defined is like this (where the desired function is
foo()...):
grep ^foo\( ck*.c
This works because the coding convention has been to make function names
always start on the left margin with their contents indented, for example:
static char *
foo(x,y) int x, y; {
...
}
Also note the style for bracket placement. This allows bracket-matching text
editors (such as EMACS) to help you make sure you know which opening bracket a
closing bracket matches, particularly when the opening bracket is above the
visible screen, and it also makes it easy to find the end of a function
(search for '}' on the left margin).
Of course EMACS tags work nicely with this format too:
$ cd kermit-source-directory
$ etags ck[cu]*.c
$ emacs
Esc-X Visit-Tags-Table<CR><CR>
(but remember that the source file for ckcpro.c is
ckcpro.w!)
Also:
- Tabs should be set every 8 spaces, as on a VT100.
- All lines must no more than 79 characters wide after tab expansion.
- Note the distinction between physical tabs (ASCII 9) and the indentation
conventions, which are: 4 for block contents, 2 for most other stuff
(obviously this is not a portability issue, just style).
[ Contents ]
[ C-Kermit ]
[ Kermit Home ]
C-Kermit was designed in 1985 as a platform-independent replacement for the
earlier Unix Kermit. C-Kermit's design was expected to promote portability,
and judging from the number of platforms to which it has been adapted since
then, the model is effective, if not ideal (obviously if we had it all to do
over, we'd change a few things). To answer the oft-repeated question: "Why
are there so many
#ifdefs?", it's because:
- Many of them are related to feature selection and program size, and so
need to be there anyway.
- Those that treat compiler, library, platform, header-file, and similar
differences have built up over time as hundreds of people all over the
world adapted C-Kermit to their particular environments and sent back
their changes. There might be more politically-correct ways to achieve
portability, but this one is natural and proven. The basic idea is to
introduce changes that can be selected by defining a symbol, which, if
not defined, leaves the program exactly as it was before the changes.
- Although it might be possible to "clean up" the "#ifdef mess",
nobody has access to all the hundreds of platforms served by the
#ifdefs to check the results.
And to answer the second-most-oft-repeated question: "Why don't you just use
GNU autoconfig / automake / autowhatever instead of hard-coding all those
#ifdefs?" Answers:
- The GNU tools are not available on all the platforms where C-Kermit must
be built and I wouldn't necessarily trust them if they were.
- Each platform is a moving target, so the tools themselves would need to
updated before Kermit could be updated.
- It would only add another layer of complexity to an already complex
process.
- Conversion at this point would not be practical unless there was a way to
test the results on all the hundreds of platforms where C-Kermit is supposed
to build.
When writing code for the system-independent C-Kermit modules, please stick to
the following coding conventions to ensure portability to the widest possible
variety of C preprocessors, compilers, and linkers, as well as certain network
and/or email transports. The same holds true for many of the "system
dependent" modules too; particularly the Unix ones, since they must be
buildable by a wide variety of compilers and linkers, new and old.
This list does not purport to be comprehensive, and although some items on it
might seem far-fetched, they would not be listed unless I had encountered them
somewhere, some time. I wish I had kept better records so I could cite
specific platforms and compilers.
- Try to keep variable and function names unique within 6 characters,
especially if they are used across modules, since 6 is the maximum for
some old linkers (actually, this goes back to TOPS-10 and -20 and other
old DEC OS's where C-Kermit never ran anyway; a more realistic maximum is
probably somewhere between 8 and 16). We know for certain that VAX C
has a 31-character max because it complains -- others might not complain,
but just silently truncate, thus folding two or more routines/variables
into one.
- Keep preprocessor symbols unique within 8 characters; that's the max for
some preprocessors (sorry, I can't give a specific example, but in 1988 or
thereabouts, I had to change character-set symbols like TC_LATIN1 and
TC_LATIN2 to TC_1LATIN and TC_2LATIN because the digits were being truncated
and ignored on a platform where I actually had to build C-Kermit 5A;
unfortunately I didn't note which platform -- maybe some early Ultrix
version?)
- Don't create preprocessor symbols, or variable or function names, that
start with underscore (_). These are usually reserved for internal
use by the compiler and header files.
- Don't put #include directives inside functions or
{ blocks }.
- Don't use the #if or #elif preprocessor constructions,
only use #ifdef, #ifndef, #define,
#undef, and #endif. C-Kermit must be buildable on old as
well as new platforms, and computers are still running whose C preprocessors
do not support these constructions. Not even within #ifdef..#endif, because
cpp reads all directives that start with # (e.g. to find the matching #endif
for the most recent #ifdef) and when it encounters an unknown directive, it
stops with a fatal error.
- Put tokens after #endif in comment
brackets, e.g. #endif /* FOO */.
- Don't indent preprocessor statements - # must always be first
char on line.
- Don't put whitespace after # in preprocessor statements.
- Don't use #pragma, even within #ifdefs -- it makes some
preprocessors give up.
- Same goes for #module, #if, etc - #ifdefs
do NOT protect them.
- Don't use logical operators in preprocessor constructions such as
"#if _XOPEN_SOURCE >= 500".
- Avoid #ifdefs inside argument list to function calls (I can't
remember why this one is here, but probably needn't be; we do this all the
time).
- Always cast strlen() in expressions to int:
if ((int)strlen(foo) < x)...
- Avoid typedefs; they might be portable but they are very confusing and
there's no way to test for their presence or absence at compile time.
Use preprocessor symbols instead if possible; at least you can test their
definitions.
- Unsigned long is not portable; use a preprocessor symbol (Kermit uses
ULONG for this).
- Long long is not portable. If you really need it, be creative.
- Similarly 1234LL is not portable, nor almost any other constant modifier
other than L.
- Unsigned char is not portable, use CHAR (a preprocessor symbol defined in
the Kermit header files) and always take precautions against character signage
(more about this below).
- Don't use initializers with automatic arrays or structs: it's not portable.
- Don't use big automatic arrays or structs in functions that might be
called recursively; some platforms have fixed-size stacks (e.g. Windows 9x:
256K) and recursive functions crash with stack overflow. Even when there is
not a compiler limitation, this causes memory to be consumed without bound,
and can end up filling swap space.
- Don't assume that struct assignment performs a copy, or that it even
exists.
- Don't use sizeof to get the size of an array; someone might come along
later and and change it from static to malloc'd. Always use a symbol to
refer to the array's size.
- Don't put prototypes for static functions into header files that are used
by modules that don't contain that function; the link step can fail with
unresolved references (e.g. on AOS/VS).
- Avoid the construction *++p (the order of evaluation varies;
it shouldn't but at least one compiler had a bug that made me include this
item).
- Don't use triple assignments, like a = b
= c = 0; (or quadruple, etc). Some
compilers generate bad code for these, or crash, etc (some version of DEC C as
I recall).
- Some compilers don't allow structure members to have the same names as
other identifiers. Try to give structure members unique names.
- Don't assume anything about order of evaluation in boolean expressions,
or that they will stop early if a required condition is not true, e.g.:
if (i > 0 && p[i-1] == blah)
can still dump core if i == 0 (hopefully this is not true of any modern
compiler, but I would not have said this if it did not actually happen
somewhere).
- Don't have a switch() statement with no cases (e.g. because of
#ifdefs); this is a fatal error in some compilers.
- Don't put lots of code in a switch case; move it out to a separate
function; some compilers run out of memory when presented with a huge
switch() statement -- it's not the number of cases that matters; it's
the overall amount of code.
- Some compilers might also limit the number of switch()
cases, e.g. to 254.
- Don't put anything between "switch() {" and
"case:" -- switch blocks are not like other blocks.
- Don't jump into or out of switches.
- Don't make character-string constants longer than about 250 bytes.
Longer strings should be broken up into arrays of strings.
- Don't write into character-string constants (obviously). Even when you
know you are not writing past the end; the compiler or linker might have put
them into read-only and/or shared memory, and/or coalesced multiple equal
constants so if you change one you change them all.
- Don't depend on '\r' being carriage return.
- Don't depend on '\n' being linefeed or for that matter any SINGLE
character.
- Don't depend on '\r' and '\n' being different (e.g. as
separate switch() cases).
- In other words, don't use \n or \r to stand for specific
characters; use \012 and \015 instead.
- Don't code for "buzzword 1.0 compliance", unless "buzzword" is K&R and
"1.0" is the first edition.
- Don't use or depend on anything_t (size_t,
pid_t, etc), except time_t, without #ifdef
protection (time_t is the only one I've found that is accepted
everywhere). This is a tough one because the same function might require
(say) a size_t arg on one platform, whereas size_t is
unheard of on another; or worse, it might require a totally different data
type, like int or long or some other typedef'd thing. It has often proved
necessary to define a symbol to stand for the type of a particular argument to
a particular library or system function to get around this problem.
- Don't use or depend on internationalization ("i18n") features,
wchar_t, locales, etc, in portable code; they are not portable.
Anyway, locales are not the right model for Kermit's multi-character-set
support. Kermit does all character-set conversion itself and does not use any
external libraries or functions.
- In particular, don't use any library functions that deal with wide
characters or Unicode in any form. These are not only nonportable, but a
constantly shifting target (e.g. the ones in glibc).
- Don't make any assumption about signal handler type. It can be void, int,
long, or anything else. Always declare signal handlers as SIGTYP (see
definition in ckcdeb.h and augment it if necessary) and always use
SIGRETURN at exit points from signal handlers.
- Signals should always be re-armed to be used again (this barely scratches
the surface -- the differences between BSD/V7 and System V and POSIX signal
handling are numerous, and some platforms do not even support signals,
alarms, or longjmps correctly or at all -- avoid all of this if you can).
- On the other hand, don't assume that signals are disarmed after being
raised. In some platforms you have to re-arm them, in others they stay
armed.
- Don't call malloc() and friends from a signal handler; don't do
anything but setting integer global variables in a signal handler.
- malloc() does not initialize allocated memory -- it never said it
did. Don't expect it to be all 0's.
- Did You Know: malloc() can succeed and the program can still dump
core later when it attempts to use the malloc'd memory? (This happens when
allocation is deferred until use and swap space is full.)
- memset(), memmove(), and memcpy() are not
portable, don't use them without protecting them in ifdefs (we have USE_MEMCPY
for this). bzero()/bcopy() too, except we're guaranteed to
have bzero()/bcopy() when using the sockets library (not
really). See examples in the source.
- Don't assume that strncpy() stops on the first null byte -- most
versions always copy the number of bytes given in arg 3, padding out with 0's
and overwriting whatever was there before. Use C-Kermit ckstrncpy()
if you want predictable non-padding behavior, guaranteed NUL-termination, and
a useful return code.
- DID YOU KNOW.. that some versions of inet_blah() routines
return IP addresses in network byte order, while others return them local
machine byte order? So passing them to ntohs() or whatever is not
always the right thing to do.
- Don't use ANSI-format function declarations without #ifdef
CK_ANSIC, and always provide an #else for the non-ANSI case.
- Use the Kermit _PROTOTYP() macro for declaring function
prototypes; it works in both the ANSI and non-ANSI cases.
- Don't depend on any other ANSI preprocessor features like "pasting" --
they are often missing or nonoperational.
- Don't assume any C++ syntax or semantics.
- Don't use // as a comment introducer. C is not C++.
- Don't declare a string as "char foo[]"
in one module and "extern char * foo"
in another, or vice-versa: this causes core dumps.
- With compiler makers falling all over themselves trying to outdo each
other in ANSI strictness, it has become increasingly necessary to cast
EVERYTHING. Especially char vs unsigned
char. We need to use unsigned chars if we want to deal with 8-bit
character sets, but most character- and string-oriented APIs want (signed)
char arguments, so explicit casts are necessary. It would be nice if
every compiler had a -funsigned-char option (as gcc does), but they
don't.
- a[x], where x is an unsigned char, can produce a
wild memory reference if x, when promoted to an int, becomes negative.
Cast it to (unsigned), even though it ALREADY IS unsigned.
- Be careful how you declare functions that have char or long arguments; for
ANSI compilers you MUST use ANSI declarations to avoid promotion problems, but
you can't use ANSI declarations with non-ANSI compilers. Thus declarations of
such functions must be hideously entwined in #ifdefs. Example:
int /* Put character in server command buffer */
#ifdef CK_ANSIC
putsrv(char c)
#else
putsrv(c) char c;
#endif /* CK_ANSIC */
/* putsrv */ {
*srvptr++ = c;
*srvptr = '\0'; /* Make sure buffer is null-terminated */
return(0);
}
- Be careful how you return characters from functions that return int values
-- "getc-like functions" -- in the ANSI world. Unless you explicitly cast the
return value to (unsigned), it is likely to be "promoted" to an int and have
its sign extended.
- At least one compiler (the one on DEC OSF/1 1.3) treats "/*" and
"*/" within string constants as comment begin and end. No amount of
#ifdefs will get around this one. You simply can't put these
sequences in a string constant, e.g. "/usr/local/doc/*.*".
- Avoid putting multiple macro references on a single line, e.g.:
putchar(BS); putchar(SP); putchar(BS)
This overflows the CPP output buffer of more than a few C preprocessors
(this happened, for example, with SunOS 4.1 cc, which evidently has a 1K
macro expansion buffer).
C-Kermit needs constant adjustment to new OS and compiler releases. Every new
OS release shuffles header files or their contents, or prototypes, or data
types, or levels of ANSI strictness, etc. Every time you make an adjustment
to remove a new compilation error, BE VERY CAREFUL to #ifdef it on a
symbol unique to the new configuration so that the previous configuration (and
all other configurations on all other platforms) remain as before.
Assume nothing. Don't assume header files are where they are supposed to be,
that they contain what you think they contain, that they define specific
symbols to have certain values -- or define them at all! Don't assume system
header files protect themselves against multiple inclusion. Don't assume that
particular system or library calls are available, or that the arguments are
what you think they are -- order, data type, passed by reference vs value,
etc. Be conservative when attempting to write portable code. Avoid all
advanced features.
If you see something that does not make sense, don't assume it's a mistake --
it might be there for a reason, and changing it or removing is likely
to cause compilation, linking, or runtime failures sometime, somewhere. Some
huge percentage of the code, especially in the platform-dependent modules, is
workarounds for compiler, linker, or API bugs.
But finally... feel free to violate any or all of these rules in
platform-specific modules for environments in which the rules are certain
not to apply. For example, in VMS-specific modules (ckv*.[ch]), it is OK to
use #if, because VAX C, DEC C, and VMS GCC all support it.
[ Contents ]
[ C-Kermit ]
[ Kermit Home ]
The C language and standard C library are notoriously inadequate and unsafe.
Strings are arrays of characters, usually referenced through pointers. There
is no native string datatype. Buffers are fixed size, and C provides no
runtime bounds checking, thus allowing overwriting of other data or even
program code. With the popularization of the Internet, the "buffer exploit"
has become a preferred method for hackers to hijack privileged programs; long
data strings are fed to a program in hopes that it uses unsafe C library calls
such as
strcpy() or
sprintf() to copy strings into automatic
arrays, thus overwriting the call stack, and therefore the routine's return
address. When such a hole is discovered, a "string" can be constructed that
contains machine code to hijack the program's privileges and penetrate the
system.
This problem is partially addressed by the strn...() routines, which
should always be used in preference to their str...() equivalents
(except when the copy operation has already been prechecked, or there is a
good reason for not using them, e.g. the sometimes undesirable side effect of
strncpy() zeroing the remainder of the buffer). The most gaping
whole, however, is sprintf(), which performs no length checking on
its destination buffer, and is not easy to replace. Although
snprintf() routines are starting to appear, they are not yet
widespread, and certainly not universal, nor are they especially portable,
or even full-featured.
For these reasons, we have started to build up our own little library of C
Library replacements, ckclib.[ch]. These are safe and highly portable
primitives for memory management and string manipulation, such as:
- ckstrncpy()
- Like strncpy but returns a useful value, doesn't zero buffer.
- ckitoa()
- Opposite of atoi()
- ckltoa()
- Opposite of atol()
- ckctoa()
- Returns character as string
- ckmakmsg()
- A safe sprintf() replacement
(but with different syntax) for up to 4 items
- ckmakxmsg()
- Like ckmakmsg() but accepts up to 12 items
More about library functions in Section 4.A.
[ Contents ]
[ C-Kermit ]
[ Kermit Home ]
This is one of the most aggravating and vexing characteristics of the C
language. By design,
chars (and
char *'s) are
SIGNED. But in the modern era we need to process characters that
can have (or include) 8-bit values, as in the ISO Latin-1, IBM CP 850, or
UTF-8 character sets, so this data must be treated as unsigned. But some C
compilers (such as those based on the Bell UNIX V7 compiler) do not support
"unsigned char" as a data type. Therefore we have the macro or typedef CHAR,
which we use when we need chars to be unsigned, but which, unfortunately,
resolves itself to "char" on those compilers that don't support "unsigned
char". AND SO... We have to do a lot of fiddling at runtime to avoid sign
extension and so forth.
Some modern compilers (e.g. IBM, DEC, Microsoft) have options that say "make
all chars be unsigned" (e.g. GCC "-funsigned-char") and we use them
when they are available. Other compilers don't have this option, and at the
same time, are becoming increasingly strict about type mismatches, and spew
out torrents of warnings when we use a CHAR where a char is
expected, or vice versa. We fix these one by one using casts, and the code
becomes increasingly ugly. But there remains a serious problem, namely that
certain library and kernel functions have arguments that are declared as
signed chars (or pointers to them), whereas our character data is unsigned.
Fine, we can can use casts here too -- but who knows what happens inside these
routines.
[ Contents ]
[ C-Kermit ]
[ Kermit Home ]
When C-Kermit is on the far end of a connection, it is said to be in
remote
mode. When C-Kermit has made a connection to another computer, it is in
local mode. (If C-Kermit is "in the middle" of a multihop connection,
it is still in local mode.)
On another axis, C-Kermit can be in any of several major states:
- Command State
- Reading and writing from the job's
controlling terminal or "console". In this mode, all i/o is handled by the
Group E conxxx() (console i/o) routines.
- Protocol State
- Reading and writing from the communications device. In this mode, all i/o
is handled by the Group E ttxxx() (terminal i/o) routines.
- Terminal State
- Reading from the keyboard with conxxx() routines and
writing to the communications device with ttxxx() routines AND
vice-versa.
When in local mode, the console and communications device are distinct.
During file transfer, Kermit may put up a file-transfer display on the console
and sample the console for interruption signals.
When in remote mode, the console and communications device are the same, and
therefore there can be no file-transfer display on the console or
interruptions from it (except for "in-band" interruptions such as ^C^C^C).
[ Contents ]
[ C-Kermit ]
[ Kermit Home ]
Library functions, strictly portable, can be used by all modules on all
platforms:
ckclib.h,
ckclib.c.
(To be filled in... For now, see Section 3.1 and
the comments in ckclib.c.)
[ Contents ]
[ C-Kermit ]
[ Kermit Home ]
The Kermit protocol kernel. These files, whose names start with "
ckc
are supposed to be totally portable C, and are expected to compile correctly
on any platform with any C compiler. "Portable" does not mean the same as as
"ANSI" -- these modules must compile on 10- and 20-year old computers, with C
preprocessors, compilers, and/or linkers that have all sorts of restrictions.
The Group B modules do not include any header files other than those that come
with Kermit itself. They do not contain any library calls except from the
standard C library (e.g.
printf()). They most certainly do not
contain any system calls. Files:
- ckcsym.h
- For use by C compilers that don't allow -D on the command line.
- ckcasc.h
- ASCII character symbol definitions.
- ckcsig.h
- System-independent signal-handling definitions and prototypes.
- ckcdeb.h
- Originally, debugging definitions. Now this file also contains
all definitions and prototypes that are shared by all modules in
all groups.
- ckcker.h
- Kermit protocol symbol definitions.
- ckcxla.h
- Character-set-related symbol definitions (see next section).
- ckcmai.c
- The main program. This module contains the declarations of all
the protocol-related global variables that are shared among the other
modules.
- ckcpro.w
- The protocol module itself, written in "wart", a lex-like
preprocessor that is distributed with Kermit under the name CKWART.C.
- ckcfns.c,
ckcfn2.c,
ckcfn3.c
- The protocol support functions used by the protocol module.
Group B modules may call upon functions from Group E, but not from Group D modules
(with the single exception that the main program invokes the user interface,
which is in Group D). (This last assertion is really only a conjecture.)
[ Contents ]
[ C-Kermit ]
[ Kermit Home ]
Character set translation tables and functions. Used by the
Group B, protocol modules, but may be specific to different
computers. (So far, all character character sets supported by C-Kermit are
supported in
ckuxla.c
and
ckuxla.h,
including Macintosh and IBM character sets). These modules should be
completely portable, and not rely on any kind of system or library services.
- ckcxla.h
- Character-set definitions usable by all versions of C-Kermit.
- ck?xla.h
- Character-set definitions for computer "?", e.g.
ckuxla.h
for UNIX,
ckmxla.h
for Macintosh.
- ck?xla
- Character-set translation tables and functions for computer "?",
For example, CKUXLA.C for UNIX, CKMXLA.C for Macintosh. So far, these are
the only two such modules. The UNIX module is used for all versions of
C-Kermit except the Macintosh version.
- ckcuni.h
- Unicode definitions
- ckcuni.c
- Unicode module
Here's how to add a new file character set in the original (non-Unicode
modules). Assuming it is based on the Roman (Latin) alphabet. Let's call it
"Barbarian". First, in ck?xla.h, add a definition for FC_BARBA (8
chars maximum length) and increase MAXFCSETS by 1. Then, in
ck?xla.c:
- Add a barbarian entry into the fcsinfo array.
- Add a "barbarian" entry to file character set keyword table, fcstab.
- Add a "barbarian" entry to terminal character set keyword table, ttcstab.
- Add a translation table from Latin-1 to barbarian: yl1ba[].
- Add a translation table from barbarian to Latin-1: ybal1[].
- Add a translation function from Barbarian to ASCII: xbaas().
- Add a translation function from Barbarian to Latin-1: xbal1().
- Add a translation function from Latin-1 to Barbarian: xl1ba().
- etc etc for each transfer character set...
- Add translation function pointers to the xls and xlr tables.
Other translations involving Barbarian (e.g. from Barbarian to Latin-Cyrillic)
are performed through these tables and functions. See ckuxla.h and
ckuxla.c for extensive examples.
To add a new Transfer Character Set, e.g. Latin Alphabet 9 (for the Euro
symbol), again in the "old" character-set modules:
- In ckcxla.h:
- Add a TC_xxxx definition and increase MAXTCSETS accordingly.
- In ck?xla.h (since any transfer charset is also a file
charset):
- Add an FC_xxxx definition and increase MAXFCSETS accordingly.
- In ck?xla.c:
- Add a tcsinfo[] entry.
- Make a tcstab[] keyword table entry.
- Make an fcsinfo[] table entry.
- Make an fcstab[] keyword table entry.
- Make a tcstab[] keyword table entry.
- If necessary, make a langinfo[] table entry.
- Make entries in the function pointer arrays.
- Provide any needed functions.
As of C-Kermit 7.0, character sets are also handled in parallel by the new
(and very large) Unicode module, ckcuni.[ch]. Eventually we should
phase out the old way, described just above, and operate entirely in (and
through) Unicode. The advantages are many. The disadvantages are size and
performance. To add a character to the Unicode modules:
- In ckcuni.h:
- In ckcuni.c:
[ Contents ]
[ C-Kermit ]
[ Kermit Home ]
This is the code that communicates with the user, gets
her commands, informs her of the results. It may be command-line oriented,
interactive prompting dialog, menus and arrow keys, windows and mice, speech
recognition, telepathy, etc. The one provided is command-and prompt, with
the ability to read commands from various sources: the console keyboard,
a file, or a macro definition. The user interface has three major functions:
- Sets the parameters for the file transfer and then starts it. This is done
by setting certain (many) global variables, such as the protocol machine start
state, the file specification, file type, communication parameters, packet
length, window size, character set, etc.
- Displays messages on the user's screen during the file transfer, using the
screen() function, which is called by the group-1 modules.
- Executes any commands directly that do not require Kermit protocol, such
as the CONNECT command, local file management commands, parameter-setting
commands, FTP client commands, etc.
If you plan to embed the Group B, files into a program
with a different user interface, your interface must supply an appropriate
screen() function, plus a couple related ones like chkint()
and intmsg() for handling keyboard (or mouse, etc) interruptions
during file transfer. The best way to find out about this is to link all the
C-Kermit modules together except the ckuu*.o and ckucon.o
modules, and see which missing symbols turn up.
C-Kermit's character-oriented user interface (as opposed to the Macintosh
version's graphical user interface) consists of the following modules.
C-Kermit can be built with an interactive command parser, a
command-line-option-only parser, a graphical user interface, or any
combination, and it can even be built with no user interface at all (in which
case it runs as a remote-mode Kermit server).
- ckucmd.h
- ckucmd.c
- The command parsing primitives used by the
interactive command parser to parse keywords, numbers, filenames, etc,
and to give help, complete fields, supply defaults, allow abbreviations
and editing, etc. This package is totally independent of Kermit, but
does depend on the
Group E functions.
- ckuusr.h
- Definitions of symbols used in Kermit's commands.
- ckuus*.c
- Kermit's interactive command parser, including the script programming
language:
ckuusr.c (includes top-level keyword tables);
ckuus2.c (HELP command text);
ckuus3.c (most of the SET command);
ckuus4.c (includes variables and functions);
ckuus[567].c (miscellaneous);
- ckuusy.c
- The command-line-option parser.
- ckuusx.c
- User interface functions common to both the interactive and
command-line parsers.
- ckuver.h
- Version heralds for different implementations.
- ckuscr.c
- The (old, uucp-like) SCRIPT command
- ckudia.c
- The DIAL command. Includes specific knowledge of many types of modems.
Note that none of the above files is actually Unix-specific. Over time they
have proven to be portable among all platforms where C-Kermit is built: Unix,
VMS, AOS/VS, Amiga, OS-9, VOS, etc etc. Thus the third letter should more
properly be "c", but changing it would be too confusing.
- ck?con.c, ckucns.c
- The CONNECT command. Terminal connection, and in some cases (Macintosh,
Windows) also terminal emulation. NOTE: As of C-Kermit 7.0, there are two
different CONNECT modules for UNIX: ckucon.c -- the traditional,
portable, fork()-based version -- and ckucns.c, a new
version that uses select() rather than forks so it can handle
encryption. ckucns.c is the preferred version for Unix;
ckucon.c is not likely to keep pace with it in terms of upgrades,
etc. However, since select() is not portable to every platform,
ckucon.c will be kept indefinitely for those platforms that can't use
ckucns.c. NOTE: SunLink X.25 support is available only in
ckucon.c.
- ck_*.*, ckuat*.*
- Modules having to do with authentication and encryption. Since the
relaxation of USA export laws, they are included with the general source-code
distribution. Secure C-Kermit binaries can be built using special targets in
the standard makefile. However, secure prebuilt binaries may not be
distributed.
For other implementations, the files may, and probably do, have different
names. For example, the Macintosh graphical user interface filenames start
with "ckm". Kermit 95 uses the ckucmd and ckuus*
modules, but has its own CONNECT command modules. And so on.
Here is a brief description of C-Kermit's "user interface interface", from
ckuusr.c. It is nowhere near complete; in particular, hundreds of
global variables are shared among the many modules. These should, some day,
be collected into classes or structures that can be passed around as needed;
not only for purity's sake, but also to allow for multiple simultaneous
communication sessions and or user interfaces. Our list of things to do is
endless, and reorganizing the source is almost always at the bottom.
The ckuus*.c modules (like many of the ckc*.c modules)
depend on the existence of C library features like fopen, fgets, feof,
(f)printf, argv/argc, etc. Other functions that are likely to vary among
operating systems -- like setting terminal modes or interrupts -- are invoked
via calls to functions that are defined in the
Group E platform-dependent
modules, ck?[ft]io.c. The command line parser processes any
arguments found on the command line, as passed to main() via
argv/argc. The interactive parser uses the facilities of the cmd package
(developed for this program, but, in theory, usable by any program). Any
command parser may be substituted for this one. The only requirements for the
Kermit command parser are these:
- Set parameters via global variables like duplex, speed, ttname, etc. See
ckcmai.c for the declarations and descriptions of these variables.
- If a command can be executed without the use of Kermit protocol, then
execute the command directly and set the sstate (start state)
variable to 0. Examples include SET commands, local directory listings, the
CONNECT command.
- If a command requires the Kermit protocol, set the following variables:
sstate string data
'x' (enter server mode) (none)
'r' (send a 'get' command) cmarg, cmarg2
'v' (enter receive mode) cmarg2
'g' (send a generic command) cmarg
's' (send files) nfils, cmarg & cmarg2 OR cmlist
'c' (send a remote host command) cmarg
cmlist is an array of pointers to strings.
cmarg, cmarg2 are pointers to strings.
nfils is an integer (hmmm, probably should be an unsigned long).
- cmarg can be:
- A filename string (possibly wild), or:
a pointer to a prefabricated generic command string, or:
a pointer to a host command string.
- cmarg2 is:
- The name to send a single file under, or:
the name under which to store an incoming file; must not be wild.
If it's the name for receiving, a null value means to store the
file under the name it arrives with.
- cmlist is:
- A list of nonwild filenames, such as passed via argv.
- nfils is an integer, interpreted as follows:
- -1: filespec (possibly wild) in cmarg, must be expanded internally.
0: send from stdin (standard input).
>0: number of files to send, from cmlist.
The screen() function is used to update the screen during file
transfer. The tlog() function writes to a transaction log (if TLOG
is defined). The debug() function writes to a debugging log (if
DEBUG is defined). The intmsg() and chkint() functions
provide the user i/o for interrupting file transfers.
[ Contents ]
[ C-Kermit ]
[ Kermit Home ]
Platform-dependent function definitions. All the Kermit modules, including
the command package, call upon these functions, which are designed to provide
system-independent primitives for controlling and manipulating devices and
files. For Unix, these functions are defined in the files
ckufio.c
(files),
ckutio.c
(communications), and
ckusig.c
(signal handling).
For VMS, the files are ckvfio.c, ckvtio.c, and
ckusig.c (VMS can use the same signal handling routines as Unix). It
doesn't really matter what the files are called, except for Kermit
distribution purposes (grouping related files together alphabetically), only
that each function is provided with the name indicated, observes the same
calling and return conventions, and has the same type.
The Group E modules contain both functions and global variables that are
accessed by modules in the other groups. These are now described.
(By the way, I got this list by linking all the C-Kermit modules together
except ckutio and ckufio. These are the symbols that ld
reported as undefined. But that was a long time ago, probably circa Version
6.)
- char *DELCMD;
- Pointer to string containing command for deleting files.
Example: char *DELCMD = "rm -f "; (UNIX)
Example: char *DELCMD = "delete "; (VMS)
Note trailing space. Filename is concatenated to end of this string.
NOTE: DELCMD is used only in versions that do not provide their own
built-in DELETE command.
- char *DIRCMD;
- Pointer to string containing command for listing files when a filespec
is given.
Example: char *DIRCMD = "/bin/ls -l "; (UNIX)
Example: char *DIRCMD = "directory "; (VMS)
Note trailing space. Filename is concatenated to end of this string.
NOTE: DIRCMD is used only in versions that do not provide their own
built-in DIRECTORY command.
- char *DIRCM2;
- Pointer to string containing command for listing files when a filespec
is not given. (currently not used, handled in another way.)
Example: char *DIRCMD2 = "/bin/ls -ld *";
NOTE: DIRCMD2 is used only in versions that do not provide their own
built-in DIRECTORY command.
- char *PWDCMD;
- Pointer to string containing command to display current directory.
Example: char *PWDCMD = "pwd ";
NOTE: PWDCMD is used only in versions that do not provide their own
built-in PWD command.
- char *SPACMD;
- Pointer to command to display free disk space in current device/directory.
Example: char *SPACMD = "df .";
NOTE: SPACMD is used only in versions that do not provide their own
built-in SPACE command.
- char *SPACM2;
- Pointer to command to display free disk space in another device/directory.
Example: char *SPACM2 = "df ";
Note trailing space. Device or directory name is added to this string.
NOTE: SPACMD2 is used only in versions that do not provide their own
built-in SPACE command.
- char *TYPCMD;
- Pointer to command for displaying the contents of a file.
Example: char *TYPCMD = "cat ";
Note trailing space. Device or directory name is added to this string.
NOTE: TYPCMD is used only in versions that do not provide their own
built-in TYPE command.
- char *WHOCMD;
- Pointer to command for displaying logged-in users.
Example: char *WHOCMD = "who ";
Note trailing space. Specific user name may be added to this string.
- int backgrd = 0;
- Flag for whether program is running in foreground (0) or background
(nonzero). Background operation implies that screen output should not be
done and that all errors should be fatal.
- int ckxech;
- Flag for who is to echo console typein:
1: The program (system is not echoing).
0: The OS, front end, terminal, etc (not this program).
- char *ckxsys;
- Pointer to string that names the computer and operating system.
Example: char *ckxsys = " NeXT Mach 1.0";
Tells what computer system ckxv applies to.
In UNIX Kermit, this variable is also used to print the program herald,
and in the SHOW VERSION command.
- char *ckxv;
- Pointer to version/edit info of ck?tio.c module.
Example: char *ckxv = "UNIX Communications Support, 6.0.169, 6 Sep 96";
Used by SHOW VERSION command.
- char *ckzsys;
- Like ckxsys, but briefer.
Example: char *ckzsys = " 4.3 BSD";
Tells what platform ckzv applies to. Used by the SHOW VERSION command.
- char *ckzv;
- Pointer to version/edit info of ck?fio.c module.
Example: char *ckzv = "UNIX File support, 6.0.113, 6 Sep 96";
Used by SHOW VERSION command.
- int dfflow;
- Default flow control.
0 = none, 1 = Xon/Xoff, ... (see FLO_xxx symbols in ckcdeb.h)
Set by Group E module.
Used by
ckcmai.c
to initialize flow control variable.
- int dfloc;
- Default location.
0 = remote, 1 = local.
Set by Group E module.
Used by ckcmai.c to initialize local variable. Used in various
places in the user interface.
- int dfprty;
- Default parity.
0 = none, 'e' = even, 'o' = odd, 'm' = mark, 's' = space.
Set by Group E module. Used by ckcmai.c to initialize parity
variable.
- char *dftty;
- Default communication device. Set by Group E module. Used in many
places. This variable should be initialized the symbol CTTNAM, which is
defined in ckcdeb.h, e.g. as "/dev/tty" for UNIX, "TT:" for
VMS, etc. Example: char *dftty = CTTNAM;
- char *mtchs[];
- Array of string pointers to filenames that matched the most recent
wildcard match, i.e. the most recent call to zxpand(). Used (at
least) by command parsing package for partial filename completion.
- int tilde_expand;
- Flag for whether to attempt to expand leading tildes in directory names
(used in UNIX only, and then only when the symbol DTILDE is defined.
- int ttnproto;
- The protocol being used to communicate over a network device. Values are
defined in ckcnet.h. Example: NP_TELNET is network protocol "telnet".
- int maxnam;
- The maximum length for a filename, exclusive of any device or directory
information, in the format of the host operating system.
- int maxpath;
- The maximum length for a fully specified filename, including device
designator, directory name, network node name, etc, in the format of
the host operating system, and including all punctuation.
- int ttyfd;
- File descriptor of the communication device. -1 if there is no open
or usable connection, including when C-Kermit is in remote mode.
Since this is not implemented everywhere, references to it are in
#ifdef CK_TTYFD..#endif.
[ Contents ]
[ C-Kermit ]
[ Kermit Home ]
These are divided into three categories: file-related functions (B.1),
communication functions (B.2), and miscellaneous functions (B.3).
In most implementations, these are collected together into a module called
ck?fio.c, where
? = "
u"
(
ckutio.c
for Unix), "
v"
(
ckvtio.c
for VMS),
etc. To be totally platform-independent, C-Kermit maintains
its own file numbers, and provides the functions described in this section to
deal with the files associated with them. The file numbers are referred to
symbolically, and are defined as follows in
ckcker.h:
#define ZCTERM 0 /* Console terminal */
#define ZSTDIO 1 /* Standard input/output */
#define ZIFILE 2 /* Current input file for SEND command */
#define ZOFILE 3 /* Current output file for RECEIVE command */
#define ZDFILE 4 /* Current debugging log file */
#define ZTFILE 5 /* Current transaction log file */
#define ZPFILE 6 /* Current packet log file */
#define ZSFILE 7 /* Current session log file */
#define ZSYSFN 8 /* Input from a system function (pipe) */
#define ZRFILE 9 /* Local file for READ command */ (NEW)
#define ZWFILE 10 /* Local file for WRITE command */ (NEW)
#define ZMFILE 11 /* Auxiliary file for internal use */ (NEW)
#define ZNFILS 12 /* How many defined file numbers */
In the descriptions below, fn refers to a filename, and n refers
to one of these file numbers. Functions are of type int unless otherwise
noted, and are listed mostly alphabetically.
- int
chkfn(n) int n;
- Checks the file number n. Returns:
-1: File number n is out of range
0: n is in range, but file is not open
1: n in range and file is open
- int
iswild(filspec) char *filespec;
- Checks if the file specification is "wild", i.e. contains metacharacters
or other notations intended to match multiple filenames. Returns:
0: not wild
1: wild.
- int
isdir(string) char *string;
- Checks if the string is the name of an existing directory. The idea is to
check whether the string can be "cd'd" to, so in some cases (e.g. DOS) it
might also indicate any file structured device, such as a disk drive (like
A:). Other nonzero returns indicate system-dependent information;
e.g. in VMS isdir("[.FOO]") returns 1 but isdir("FOO.DIR;1")
returns 2 to indicate the directory-file name is in a format that needs
conversion before it can be combined with a filename. Returns:
0: not a directory (including any kind of error)
1: it is an existing directory
- char *
zfcdat(name) char *name;
- Returns modification (preferably, otherwise creation) date/time of file
whose name is given in the argument string. Return value is a pointer to a
string of the form yyyymmdd hh:mm:ss, for example 19931231 23:59:59, which
represents the local time (no timezone or daylight savings time finagling
required). Returns the null string ("") on failure. The text pointed to by
the string pointer might be in a static buffer, and so should be copied to a
safe place by the caller before any subsequent calls to this function.
- struct zfnfp *
zfnqfp(fn, buflen, buf) char * fn; int buflen; char * buf;
- Given the filename fn, the corresponding fully qualified,
absolute filename is placed into the buffer buf, whose length is buflen. On
failure returns a NULL pointer. On success returns a pointer to a struct
zfnfp containing pointers to the full pathname and to just the filename, and
an int giving the length of the full pathname. All references to this
function in mainline code must be protected by
#ifdef ZFNQFP..#endif, because it is not present in all of the
ck*fio.c modules. So if you implement this function in a version
that did not have it before, be sure to add #define ZFNQFP in the appropriate
spot in ckcdeb.h or in the build-procedure CFLAGS.
- int
zcmpfn(s1,s2) char * s2, * s2;
- Compares two filenames to see if they refer to the same. Internally, the
arguments can be converted to fully qualified pathnames, e.g. with
zfnqfp(), realpath(), or somesuch. In Unix or other systems
where symbolic links exist, the link should be resolved before making the
comparison or looking at the inodes. Returns:
0: Files are not identical.
1: Files are identical.
- int
zfseek(pos) long pos;
- Positions the input pointer on the current input file to the given
position. The pos argument is 0-based, the offset (distance in bytes) from
beginning of the file. Needed for RESEND, PSEND, and other recovery
operations. This function is not necessarily possible on all systems,
e.g. record-oriented systems. It should only be used on binary files
(i.e. files we are sending in binary mode) and stream-oriented file systems.
Returns:
-1: on failure.
0: On success.
- int
zchdir(dirnam) char *dirnam;
- Changes current or default directory to the one given in dirnam.
Returns:
0: On failure.
1: on success.
- long
zchki(fn) char *fn;
- Check to see if file with name fn is a regular, readable, existing file,
suitable for Kermit to send -- not a directory, not a symbolic link, etc.
Returns:
-3: if file exists but is not accessible (e.g.
read-protected);
-2: if file exists but is not of a readable type (e.g. a
directory);
-1: on error (e.g. file does not exist, or fn is garbage);
>=0: (length of file) if file exists and is readable.
Also see isdir(), zgetfs().
- int
zchkpid(pid) unsigned long pid;
- Returns:
1: If the given process ID (e.g. pid in UNIX) is valid
and active
0: otherwise.
- long
zgetfs(fn) char *fn;
- Gets the size of the given file, regardless of accessibility. Used for
directory listings. Unlike zchki(), should return the size of any kind
of file, even a directory. zgetfs() also should serve as a mini "get
file info" function that can be used until we design a better one, by
also setting some global variables:
int zgfs_link = 1/0 =
file is (not) a symbolic link.
int zgfs_dir = 1/0 =
file is (not) a directory.
char linkname[] = if zgfs_link != 0,
name of file link points to.
Returns:
-1: on error (e.g. file does not exist, or fn is garbage);
>=0: (length of file) if file exists and is readable.
- int
zchko(fn) char *fn;
- Checks to see if a file of the given name can be created. Returns:
-1: if file cannot be created, or on any kind of error.
0: if file can be created.
- int
zchkspa(fn,len) char *f; long len;
- Checks to see if there is sufficient space to store the file named
fn, which is len bytes long.
If you can't write a function to do this, then just make a dummy that
always returns 1; higher level code will recover from disk-full errors.
The receiving Kermit uses this function to refuse an incoming file based
on its size, via the attribute mechanism. Returns:
-1: on error.
0: if there is not enough space.
1: if there is enough space.
- int
zchin(n,c) int n; int *c;
- Gets a character from file number n, return it in c (call with &c).
Returns:
-1: on failure, including EOF.
0: on success with character in c.
- int
zchout(n,c) int n; char c;
- Writes the character c to file number n. Returns:
-1: on error.
0: on success.
- int
zclose(n) int n;
- Closes file number n. Returns:
-1: on error.
1: on success.
- int
zdelet(fn) char *name;
- Attempts to delete (remove, erase) the named file. Returns:
-1: on error.
1: if file was deleted successfully.
- char *
zgperm(char * f)
- Returns a pointer to the system-dependent numeric permissions/protection
string for file f, or NULL upon failure. Used if CK_PERMS is defined.
- char *
ziperm(char * f)
- Returns a pointer to the system-dependent symbolic permissions/protection
string for file f, or NULL upon failure. Used if CK_PERMS is defined.
Example: In UNIX zgperm(f) might return "100770", but ziperm() might
return "-rwxrwx---". In VMS, zgperm() would return a
hexadecimal string, but ziperm() would return something like
"(RWED,RWED,RE,)".
- char *
zgtdir()
- Returns a pointer to the name of the current directory, folder, etc, or a
NULL pointer if the current directory cannot be determined. If possible, the
directory specification should be (a) fully specified, e.g. as a
complete pathname, and (b) be suitable for appending a filename. Thus,
for example, Unix directory names should end with '/'. VMS directory names
should look like DEV:[NAME] (rather than, say, NAME.DIR;1).
- char *
zhome()
- Returns a pointer to a string containing the user's home directory, or NULL
upon error. Should be formatted like zgtdir() (q.v.).
- int
zinfill()
- Fill buffer from input file. This function is used by the macro
zminchar(), which is defined in ckcker.h.
zminchar() manages its own buffer, and calls zinfill() to
fill it whenever it becomes empty. It is used only for sending files, and
reads characters only from file number ZIFILE. zinfill() returns -1
upon end of file, -2 upon fatal error, and -3 upon timeout (e.g. when reading
from a pipe); otherwise it returns the first character from the buffer it just
read.
- int
zkself()
- Kills the current job, session, process, etc, logs out, disappears. Used
by the Kermit server when it receives a BYE command. On failure, returns -1.
On success, does not return at all! This function should not be called until
all other steps have been taken to close files, etc.
- VOID
zstrip(fn,&fn2) char *fn1, **fn2;
- Strips device and directory, etc, from file specification fn,
leaving only the filename (including "extension" or "filetype" -- the part
after the dot). For example DUA0:[PROGRAMS]OOFA.C;3 becomes
OOFA.C, or /usr/fdc/oofa.c becomes oofa.c. Returns
a pointer to result in fn2.
- int
zsetperm(char * file, unsigned int code)
- Set permissions of file to given system-dependent code.
0: On failure.
1: on success.
- int
zsetroot(char * dir)
- Sets the root for the user's file access, like Unix chroot(), but does
not require privilege. In Unix, this must be implemented entirely by
Kermit's own file access routines. Returns:
1: Success
-1: Invalid argument
-2:
-3: Internal error
-4: Access to given directory denied
-5: New root not within old root
- int
zinroot(char * file)
- If no root is set (zsetroot()), returns 1.
Otherwise, if given file is in the root, returns 1.
Otherwise, returns 0.
- VOID
zltor(fn,fn2) char *fn1, *fn2;
- Local-To-Remote filename translation. OBSOLETE: replaced by
nzltor() (q.v.). Translates the local filename fn into
a format suitable for transmission to an arbitrary type of computer, and
copies the result into the buffer pointed to by fn2. Translation may
involve (a) stripping the device and/or directory/path name, (b)
converting lowercase to uppercase, (c) removing spaces and strange
characters, or converting them to some innocuous alphabetic character like X,
(d) discarding or converting extra periods (there should not be more
than one). Does its best. Returns no value. name2 is a pointer to a buffer,
furnished by the caller, into which zltor() writes the resulting
name. No length checking is done.
- #ifdef NZLTOR
VOID
nzltor(fn,fn2,convert,pathnames,max)
char *fn1,*fn2; int convert,pathnames,max;
- Replaces zltor(). This new version handles pathnames and checks
length. fn1 and fn2 are as in zltor(). This version
is called unconditionally for each file, rather than only when filename
conversion is enabled. Pathnames can have the following values:
PATH_OFF: Pathname, if any, is to be stripped
PATH_REL: The relative pathname is to be included
PATH_ABS: The full pathname is to be included
After handling pathnames, conversion is done to the result as in the
zltor()
description if convert != 0; if relative or absolute pathnames are included,
they are converted to UNIX format, i.e. with slash (/) as the
directory separator. The max parameter specifies the maximum size of
fn2. If convert > 0, the regular conversions are done; if convert
< 0, minimal conversions are done (we skip uppercasing the letters, we
allow more than one period, etc; this can be used when we know our partner is
UNIX or similar).
- #endif /* NZLTOR */
- int
nzxpand(fn,flags) char *fn; int flags;
- Replaces zxpand(), which is obsolete as of C-Kermit 7.0.
Call with:
fn = Pointer to filename or pattern.
flags = option bits:
flags & ZX_FILONLY
Match regular files
flags & ZX_DIRONLY
Match directories
flags & ZX_RECURSE
Descend through directory tree
flags & ZX_MATCHDOT
Match "dot files"
flags & ZX_NOBACKUP
Don't match "backup files"
flags & ZX_NOLINKS
Don't follow symlinks.
Returns the number of files that match fn, with data structures set up
so the first file (if any) will be returned by the next znext() call.
If ZX_FILONLY and ZX_DIRONLY are both set, or neither one is set, files and
directories are matched. Notes:
- It is essential that the number returned by nzxpand() reflect
the actual number of filenames that will be returned by znext()
calls. In other words:
for (n = nzxpand(string,flags); n > 0; n--) {
znext(buf);
printf("%s\n", buf);
}
should print all the file names; no more, no less.
- In UNIX, DOS, OS-9, etc, where directories contain entries for themselves
(.) and the superior directory (..), these should NOT be
included in the list under any circumstances, including when ZX_MATCHDOT is
set.
- Additional option bits might be added in the future, e.g. for
sorting (sort by date/name/size, reverse/ascending, etc). Currently this
is done only in higher level code (through a hack in which the
nzxpand() exports its filename array, which is not portable because
not all OS's can use this mechanism).
- int
zmail(addr,fn) char *addr, fn;
- Send the local, existing file fn as e-mail to the address addr.
Returns:
0: on success
2: if mail delivered but temp file can't be deleted
-2: if mail can't be delivered
- int
zmkdir(path) char *path;
- The path can be a file specification that might contain directory
information, in which the filename is expected to be included, or an
unambiguous directory specification (e.g. in UNIX it must end with "/").
This routine attempts to create any directories in the given path
that don't already exist. Returns 0 or greater success: no directories needed
creation, or else all directories that needed creation were created
successfully; the return code is the number of directories that were
created. Returns -1 on failure to create any of the needed directories.
- int
zrmdir(path) char *path;
- Attempts to remove the given directory. Returns 0 on success, -1 on
failure. The detailed semantics are open -- should it fail if the directory
contains any files or subdirectories, etc. It is probably best for this
routine to behave in whatever manner is customary on the underlying
platform; e.g. in UNIX, VMS, DOS, etc, where directories can not be removed
unless they are empty.
- VOID
znewn(fn,s) char *fn, **s;
- Transforms the name fn into a filename that is guaranteed to be
unique. If the file fn does not exist, then the new name is the same
as fn; Otherwise, it's different. this function does its best, returns
no value. New name is created in caller's space. Call like this:
znewn(old,&new);. The second parameter is a pointer to the new name.
This pointer is set by znewn() to point to a static string in its own
space, so be sure to the result to a safe place before calling this function
again.
- int
znext(fn) char *fn;
- Copies the next file name from a file list created by zxpand()
into the string pointed to by fn (see zxpand). If no more files,
then the null string is placed there. Returns 0 if there are no more
filenames, with 0th element the array pointed to by fn set to NUL. If
there is a filename, it is stored in the array pointed to by fn and a
positive number is returned. NOTE: This is a change from earlier definitions
of this function (pre-1999), which returned the number of files remaining;
thus 0 was the return value when returning the final file. However, no
mainline code ever depended on the return value, so this change should be
safe.
- int
zopeni(n,fn) int n; char *fn;
- Opens the file named fn for input as file number n. Returns:
0: on failure.
1: on success.
- int
zopeno(n,fn,zz,fcb)
int n; char *name; struct zattr *zz; struct filinfo *fcb;
- Attempts to open the named file for output as file number n.
zz is a Kermit file attribute structure as defined in ckcdeb.h,
containing various information about the file, including its size, creation
date, and so forth. This function should attempt to honor as many of these as
possible. fcb is a "file control block" in the traditional sense,
defined in ckcdeb.h, containing information relevant to complicated
file systems like VMS (RMS), IBM MVS, etc, like blocksize, record length,
organization, record format, carriage control, etc. Returns:
0: on failure.
1: on success.
- int
zoutdump()
- Dumps a file output buffer. Used with the macro zmchout()
defined in ckcker.h. Used only with file number ZOFILE, i.e. the
file that is being received by Kermit during file transfer. Returns:
-1: on failure.
0: on success.
- int
zprint(p,fn) char *p, *f;
- Prints the file with name fn on a local printer, with options p.
Returns:
0: on success
3: if file sent to printer but can't be deleted
-3: if file can't be printed
- int
zrename(fn,fn2) char *fn, *fn2;
- Changes the name of file fn to fn2. If fn2 is the
name of an existing directory, or a file-structured device, then file
fn is moved to that directory or device, keeping its original name. If
fn2 lacks a directory separator when passed to this function, an
appropriate one is supplied. Returns:
-1: on failure.
0: on success.
- int
zcopy(source,dest) char * source, * dest;
- Copies the source file to the destination. One file only.
No wildcards. The destination string may be a filename or a directory
name. Returns:
0: on success.
<0: on failure:
-2: source file is not a regular file.
-3: source file not found.
-4: permission denied.
-5: source and destination are the same file.
-6: i/o error.
-1: other error.
- char *
zlocaltime(char *)
- Call with: "yyyymmdd hh:mm:ss" GMT/UTC date-time.
Returns pointer to local date-time string "yyyymmdd hh:mm:ss"
on success, NULL on failure.
- VOID
zrtol(fn,fn2) char *fn, *fn2;
- Remote-To-Local filename translation. OBSOLETE: replaced by
nzrtol(). Translates a "standard" filename to a local filename. For
example, in Unix this function might convert an all-uppercase name to
lowercase, but leave lower- or mix-case names alone. Does its best, returns
no value. New name is in string pointed to by fn2. No length checking is
done.
- #ifdef NZLTOR
- int
nzrtol(fn,fn2,convert,pathnames,max) char *fn1,*fn2; int convert,pathnames,max;
- Replaces zrtol. Like zrtol but handles pathnames and checks length. See
nzltor for detailed description of parameters.
- #endif /* NZLTOR */
- int
zsattr(xx) struct zattr *xx;
- Fills in a Kermit file attribute structure for the file which is to be
sent, namely the currently open ZIFILE. Note that this is not a very good
design, but we're stuck with it. Callers must ensure that zsattr() is called
only on real files, not on pipes, internally generated file-like objects such
as server REMOTE command responses, etc. Returns:
-1: on failure.
0: on success with the structure filled in.
If any string member is null, it should be ignored by the caller.
If any numeric member is -1, it should be ignored by the caller.
- int
zshcmd(s) char *s;
- s contains to pointer to a command to be executed by the host computer's
shell, command parser, or operating system. If the system allows the user to
choose from a variety of command processors (shells), then this function
should employ the user's preferred shell. If possible, the user's job
(environment, process, etc) should be set up to catch keyboard interruption
signals to allow the user to halt the system command and return to Kermit.
The command must run in ordinary, unprivileged user mode. If possible, this
function should return -1 on failure to start the command, or else it should
return 1 if the command succeeded and 0 if it failed.
- int
pexitstatus
- zshcmd() and zsyscmd() should set this to the command's
actual exit status code if possible.
- int
zsyscmd(s) char *s;
- s contains to pointer to a command to be executed by the host computer's
shell, command parser, or operating system. If the system allows the user to
choose from a variety of command processors (shells), then this function
should employ the system standard shell (e.g. /bin/sh for Unix), so that the
results will always be the same for everybody. If possible, the user's job
(environment, process, etc) should be set up to catch keyboard interruption
signals to allow the user to halt the system command and return to Kermit.
The command must run in ordinary, unprivileged user mode. If possible, this
function should return -1 on failure to start the command, or else it should
return 1 if the command succeeded and 0 if it failed.
- VOID
z_exec(s,args) char * s; char * args[];
- This one executes the command s (which is searched for using the system's
normal searching mechanism, such as PATH in UNIX), with the given argument
vector, which follows the conventions of UNIX argv[]: the name of the
command pointed to by element 0, the first arg by element 1, and so on. A
null args[] pointer indicates the end of the argument list. All open
files must remain open so the exec'd process can use them. Returns only if
unsuccessful.
- int
zsinl(n,s,x) int n, x; char *s;
- Reads a line from file number n.
Writes the line into the address s provided by the caller.
Writing terminates when newline is read, but with newline discarded.
Writing also terminates upon EOF or if length x is exhausted.
Returns:
-1: on EOF or error.
0: on success.
- int
zsout(n,s) int n; char *s;
- Writes the string s out to file number n. Returns:
-1: on failure.
0: on success.
- int
zsoutl(n,s) int n; char *s;
- Writes the string s out to file number n and adds a line (record)
terminator (boundary) appropriate for the system and the file format.
Returns:
-1: on failure.
0: on success.
- int
zsoutx(n,s,x) int n, x; char *s;
- Writes exactly x characters from string s to file number n.
If s has fewer than x characters, then the entire string s is written.
Returns:
-1: on failure.
>= 0: on success,
the number of characters actually written.
- int
zstime(fn,yy,x) char *fn; struct zattr *yy; int x;
- Sets the creation date (and other attributes) of an existing file, or
compares a file's creation date with a given date. Call with:
fn:
| pointer to name of existing file.
|
yy:
| Pointer to a Kermit file attribute structure in which
yy->date.val is a date of the form
yyyymmdd hh:mm:ss, e.g. 19900208 13:00:00, which is
to be used for setting or comparing the file date. Other attributes in the
struct can also be set, such as the protection/permission (See
Appendix I),
when it makes sense (e.g. "yy->lprotect.val" can be set if the
remote system ID matches the local one).
|
x:
| A function code: 0 means to set the file's creation date as given.
1 means compare the date from the yy struct with the file's date.
|
Returns:
-1: on any kind of error.
0: if x is 0 and the file date was set successfully.
0:
if x is 1 and date from attribute structure > file creation date.
1:
if x is 1 and date from attribute structure <= file creation date.
- VOID
zstrip(name,name2) char *name, **name2;
- Strips pathname from filename "name". Constructs the resulting string
in a static buffer in its own space and returns a pointer to it in name2.
Also strips device name, file version numbers, and other "non-name" material.
- int
zxcmd(n,s) char *s;
- Runs a system command so its output can be accessed as if it were file n.
The command is run in ordinary, unprivileged user mode.
If n is ZSTDIO or ZCTERM, returns -1.
If n is ZIFILE or ZRFILE, then Kermit reads from the command, otherwise
Kermit writes to the command.
Returns 0 on error, 1 on success.
- int
zxpand(fn) char *fn;
- OBSOLETE: Replaced by nzxpand(), q.v.
- #ifdef ZXREWIND
- int
zxrewind()
- Returns the number of files returned by the most recent nzxpand()
call, and resets the list to the beginning so the next znext() call
returns the first file. Returns -1 if zxpand has not yet been called. If
this function is available, ZXREWIND should be defined; otherwise it should
not be referenced.
- #endif /* ZXREWIND */
- int
xsystem(cmd) char *cmd;
- Executes the system command without redirecting any of its i/o, similar
(well, identical) to system() in Unix. But before passing the
command to the system, xsystem() ensures that all privileges are
turned off, so that the system command executes in ordinary unprivileged user
mode. If possible, xsystem() returns the return code of the command
that was executed.
These must be implemented in any C-Kermit version that is to be installed as
an Internet Kermit Service Daemon (IKSD). IKSD is expected to be started by
the Internet Daemon (e.g. inetd) with its standard i/o redirected to the
incoming connection.
- int ckxanon;
- Nonzero if anonymous logins allowed.
- extern int inserver;
- Nonzero if started in IKSD mode.
- extern int isguest;
- Nonzero if IKSD and user logged in anonymously.
- extern char * homdir;
- Pointer to user's home directory.
- extern char * anonroot;
- Pointer to file-system root for anonymous users.
Existing functions must make "if (inserver && isguest)" checks for
actions that would not be legal for guests: zdelete(),
zrmdir(), zprint(), zmail(), etc.
- int
zvuser(name) char * name;
- Verifies that user "name" exists and is allowed to log in. If the name is
"ftp" or "anonymous" and ckxanon != 0, a guest login is set up. Returns 0
if user not allowed to log in, nonzero if user may log in.
- int
zvpass(string) char * string;
- Verifies password of the user from the most recent zvuser() call.
Returns nonzero if password is valid for user, 0 if it isn't. Makes any
appropriate system log entries (IKSD logins, failed login attempts, etc). If
password is valid, logs the user in as herself (if real user), or sets up
restricted anonymous access if user is guest (e.g. changes file-system root to
anonroot and sets isguest = 1).
- VOID
zsyslog()
- Begins any desired system logging of an IKSD session.
- VOID
zvlogout()
- Terminates an IKSD session. In most cases this is simply a wrapper for
exit() or doexit(), with some system logging added.
These functions are used by C-Kermit to adapt itself to operating systems
where the program can be made to run in a "privileged" mode, e.g. setuid or
setgid in Unix. C-Kermit should NOT read and write files or start
subprocesses as a privileged program. This would present a serious threat to
system security. The security package has been installed to prevent such
security breaches by turning off the program's special privileges at all times
except when they are needed.
In UNIX, the only need Kermit has for privileged status is access to the UUCP
lockfile directory, in order to read, create, and destroy lockfiles, and to
open communication devices that are normally protected against the user (see
the Unix C-Kermit Installation Instructions for
discussion). Therefore, privileges should only be enabled for these
operations and disabled at all other times. This relieves the programmer of
the responsibility of putting expensive and unreliable access checks around
every file access and subprocess creation.
Strictly speaking, these functions are not required in all C-Kermit
implementations, because their use (so far, at least) is internal to the Group
E modules. However, they should be included in all C-Kermit implementations
for operating systems that support the notion of a privileged program (UNIX,
RSTS/E, what others?).
- int
priv_ini()
- Determine whether the program is running in privileged status. If so,
turn off the privileges, in such a way that they can be turned on again
when needed. Called from sysinit() at program startup time.
Returns:
0 on success
nonzero on failure, in which case the program should
halt immediately.
- int
priv_on()
- If the program is not privileged, this function does nothing. If the
program is privileged, this function returns it to privileged status.
priv_ini() must have been called first. Returns:
0 on success
nonzero on failure
- int
priv_off()
- Turns privileges off (if they are on) in such a way that they can be
turned back on again. Returns:
0 on success
nonzero on failure
- int
priv_can()
- Turns privileges off in such a way that they cannot be turned back on.
Returns:
0 on success
nonzero on failure
- int
priv_chk()
- Attempts to turns privileges off in such a way that they can be turned on
again later. Then checks to make sure that they were really turned off.
If they were not really turned off, then they are canceled permanently.
Returns:
0 on success
nonzero on failure
These relate to the program's "console", or controlling terminal, i.e. the
terminal that the user is logged in on and types commands at, or on a PC or
workstation, the actual keyboard and screen.
- int
conbin(esc) char esc;
- Puts the console into "binary" mode, so that Kermit's command parser can
control echoing and other treatment of characters that the user types.
esc is the character that will be used to get Kermit's attention during
packet mode; puts this in a global place. Sets the ckxech variable.
Returns:
-1: on error.
0: on success.
- int
concb(esc) char esc;
- Put console in "cbreak" (single-character wakeup) mode. That is, ensure
that each console character is available to the program immediately when the
user types it. Otherwise just like conbin(). Returns:
-1: on error.
0: on success.
- int
conchk()
- Returns a number, 0 or greater, the number of characters waiting to be read
from the console, i.e. the number of characters that the user has typed that
have not been read yet by Kermit.
- long
congspd();
- Returns the speed ("baud rate") of the controlling terminal, if known,
otherwise -1L.
- int
congks(timo) int timo;
- Get Keyboard Scancode. Reads a keyboard scan code from the physical
console keyboard. If the timo parameter is greater than zero, then times out
and returns -2 if no character appears within the given number of seconds.
Upon any other kind of error, returns -1. Upon success returns a scan code,
which may be any positive integer. For situations where scan codes cannot be
read (for example, when an ASCII terminal is used as the job's controlling
terminal), this function is identical to coninc(), i.e. it returns an
8-bit character value. congks() is for use with workstations whose
keyboards have Alternate, Command, Option, and similar modifier keys, and
Function keys that generate codes greater than 255.
- int
congm()
- Console get modes. Gets the current console terminal modes and saves them
so that conres() can restore them later. Returns 1 if it got the
modes OK, 0 if it did nothing (e.g. because Kermit is not connected with any
terminal), -1 on error.
- int
coninc(timo) int timo;
- Console Input Character. Reads a character from the console. If the timo
parameter is greater than zero, then coninc() times out and returns
-2 if no character appears within the given number of seconds. Upon any other
kind of error, returns -1. Upon success, returns the character itself, with a
value in the range 0-255 decimal.
- VOID
conint(f,s) SIGTYP (*f)(), (*s)();
- Sets the console to generate an interrupt if the user types a keyboard
interrupt character, and to transfer control the signal-handling function f.
For systems with job control, s is the address of the function that suspends
the job. Sets the global variable "backgrd" to zero if Kermit is running in
the foreground, and to nonzero if Kermit is running in the background. See
ckcdeb.h for the definition of SIGTYP. No return value.
- VOID
connoi()
- Console no interrupts. Disable keyboard interrupts on the console.
No return value.
- int
conoc(c) char c;
- Writes character c to the console terminal. Returns:
0 on failure, 1 on success.
- int
conol(s) char *s;
- Writes string s to the console. Returns -1 on error, 0 or greater on
success.
- int
conola(s) char *s[]; {
- Writes an array of strings to the console. Returns -1 on error, 0 or
greater on success.
- int
conoll(s) char *s;
- Writes string s to the console, followed by the necessary line termination
characters to put the console cursor at the beginning of the next line.
Returns -1 on error, 0 or greater on success.
- int
conres()
- Restores the console terminal to the modes obtained by congm().
Returns: -1 on error, 0 on success.
- int
conxo(x,s) int x; char *s;
- Write x characters from string s to the console. Returns 0 or greater on
success, -1 on error.
- char *
conkbg();
- Returns a pointer to the designator of the console keyboard type.
For example, on a PC, this function would return "88", "101", etc.
Upon failure, returns a pointer to the empty string.
The communication device is the device used for terminal emulation and file
transfer. It may or may not be the same device as the console, and it may or
may not be a terminal (serial-port) device; it could also be a network
connection. For brevity, the communication device is referred to here as the
"tty". When the communication device is the same as the console device,
Kermit is said to be in remote mode. When the two devices are different,
Kermit is in local mode.
- int
ttchk()
- Returns the number of characters that have arrived at the communication
device but have not yet been read by ttinc(), ttinl(), and
friends. If communication input is buffered (and it should be), this is the
sum of the number of unread characters in Kermit's buffer PLUS the number of
unread characters in the operating system's internal buffer. The call must be
nondestructive and nonblocking, and as inexpensive as possible. Returns:
0: or greater on success,
0: in case of internal error,
-1: or less when it determines the connection has been
broken, or there is no connection.
That is, a negative return from ttchk() should reliably indicate that
there is no usable connection. Furthermore, ttchk() should be
callable at any time to see if the connection is open. When the connection is
open, every effort must be made to ensure that ttchk returns an accurate
number of characters waiting to be read, rather than just 0 (no characters) or
1 (1 or more characters), as would be the case when we use select().
This aspect of ttchk's operation is critical to successful operation of
sliding windows and streaming, but "nondestructive buffer peeking" is an
obscure operating system feature, and so when it is not available, we have to
do it ourselves by managing our own internal buffer at a level below
ttinc(), ttinl(), etc, as in the UNIX version (non-FIONREAD
case).
An external global variable, clsondisc, if nonzero, means that if a
serial connection drops (carrier on-to-off transition detected by
ttchk()), the device should be closed and released automatically.
- int
ttclos()
- Closes the communication device (tty or network). If there were any kind
of exclusive access locks connected with the tty, these are released. If the
tty has a modem connection, it is hung up. For true tty devices, the original
tty device modes are restored. Returns:
-1: on failure.
0: on success.
- int
ttflui()
- Flush communications input buffer. If any characters have arrived but have
not yet been read, discard these characters. If communications input is
buffered by Kermit (and it should be), this function flushes Kermit's buffer
as well as the operating system's internal input buffer.
Returns:
-1: on failure.
0: on success.
- int
ttfluo()
- Flush tty output buffer. If any characters have been written but not
actually transmitted (e.g. because the system has been flow-controlled),
remove them from the system's output buffer. (Note, this function is
not actually used, but it is recommended that all C-Kermit programmers
add it for future use, even if it is only a dummy function that returns 0
always.)
- int
ttgmdm()
- Looks for the modem signals CTS, DSR, and CTS, and returns those that are
on in as its return value, in a bit mask as described for ttwmdm,
in which a bit is on (1) or off (0) according to whether the corresponding
signal is on (asserted) or off (not asserted). Return values:
-3: Not implemented
-2: if the line does not have modem control
-1: on error
>=0: on success, with bit mask containing the modem signals.
- long
ttgspd()
- Returns the current tty speed in BITS (not CHARACTERS) per second, or -1
if it is not known or if the tty is really a network, or upon any kind of
error. On success, the speed returned is the actual number of bits per
second, like 1200, 9600, 19200, etc.
- int
ttgwsiz()
- Get terminal window size. Returns -1 on error, 0 if the window size can't
be obtained, 1 if the window size has been successfully obtained. Upon
success, the external global variables tt_rows and tt_cols are set to the
number of screen rows and number of screen columns, respectively. As this
function is not implemented in all ck*tio.c modules, calls to it must be
wrapped in #ifdef CK_TTGWSIZ..#endif. NOTE: This function must
be available to use the TELNET NAWS feature (Negotiate About Window Size) as
well as Rlogin.
- int
tthang()
- Hang up the current tty device. For real tty devices, turn off DTR for
about 1/3-1/2 second (or other length of time, depending on the system).
If the tty is really a network connection, close it. Returns:
-1: on failure.
0: if it does not even try to hang up.
1: if it believes it hung up successfully.
- VOID
ttimoff()
- Turns off all pending timer interrupts.
- int
ttinc(timo) int timo; (function is old, return codes are new)
- Reads one character from the communication device. If timo is greater than
zero, wait the given number of seconds and then time out if no character
arrives, otherwise wait forever for a character. Returns:
-3: internal error (e.g. tty modes set wrong)
-2: communications disconnect
-1: timeout or other error
>=0: the character that was read.
It is HIGHLY RECOMMENDED that ttinc() be internally buffered so that
calls to it are relatively inexpensive. If it is possible to to implement
ttinc() as a macro, all the better, for example something like:
#define ttinc(t) ( (--txbufn >= 0) ? txbuf[ttbufp++] : txbufr(t) )
(see description of txbufr() below)
- int
ttinl(dest,max,timo,eol,start,turn)
int max,timo,turn; CHAR *dest, eol, start;
- ttinl() is Kermit's packet reader. Reads a packet from the
communications device, or up to max characters, whichever occurs first. A
line is a string of characters starting with the start character up to and
including the character given in eol or until the length is exhausted, or, if
turn != 0, until the line turnaround character (turn) is read. If turn is 0,
ttinl() *should* use the packet length field to detect the end, to
allow for the possibility that the eol character appears unprefixed in the
packet data. (The turnaround character is for half-duplex linemode
connections.)
If timo is greater than zero, ttinl() times out if the eol character
is not encountered within the given number of seconds and returns -1.
The characters that were input are copied into "dest" with their parity bits
stripped if parity is not none. The first character copied into dest should
be the start character, and the last should be the final character of the
packet (the last block check character). ttinl() should also absorb
and discard the eol and turn characters, and any other characters that are
waiting to be read, up until the next start character, so that subsequent
calls to ttchk() will not succeed simply because there are some
terminators still sitting in the buffer that ttinl() didn't read.
This operation, if performed, MUST NOT BLOCK (so if it can't be performed in a
guaranteed nonblocking way, don't do it).
On success, ttinl() returns the number of characters read.
Optionally, ttinl() can sense the parity of incoming packets. If it
does this, then it should set the global variable ttprty accordingly.
ttinl() should be coded to be as efficient as possible, since it is
at the "inner loop" of packet reception. ttinl() returns:
-1: Timeout or other possibly correctable error.
-2: Interrupted from keyboard.
-3: Uncorrectable i/o error -- connection lost, configuration
problem, etc.
>=0: on success, the number of characters that were actually read
and placed in the dest buffer, not counting the trailing null.
- int
ttoc(c) char c;
- Outputs the character c to the communication line. If the operation fails
to complete within two seconds, this function returns -1. Otherwise it
returns the number of characters actually written to the tty (0 or 1). This
function should only be used for interactive, character-mode operations, like
terminal connection, script execution, dialer i/o, where the overhead of the
signals and alarms does not create a bottleneck. (THIS DESCRIPTION NEEDS
IMPROVEMENT -- If the operation fails within a "certain amount of time"...
which might be dependent on the communication method, speed, etc. In
particular, flow-control deadlocks must be accounted for and broken out of
to prevent the program from hanging indefinitely, etc.)
- int
ttol(s,n) int n; char *s;
- Kermit's packet writer. Writes the n characters of the string pointed to
to by s. NOTE: It is ttol's responsibility to write ALL of the characters,
not just some of them. Returns:
-1: on a possibly correctable error (so it can be
retried).
-3: on a fatal error, e.g. connection lost.
>=0: on success, the actual number of characters written (the
specific number is not actually used for anything).
- int
ttopen(ttname,lcl,modem,timo) char *ttname; int *lcl, modem, timo;
- Opens a tty device, if it is not already open. ttopen must check to make
sure the SAME device is not already open; if it is, ttopen returns
successfully without doing anything. If a DIFFERENT device is currently open,
ttopen() must call ttclos() to close it before opening the
new one.
- Parameters:
- ttname:
- character string - device name or network host name.
- lcl:
- If called with lcl < 0,
sets value of lcl as follows:
0: the terminal named by ttname is the job's controlling terminal.
1: the terminal named by ttname is not the job's controlling terminal.
If the device is already open, or if the requested device can't
be opened, then lcl remains (and is returned as) -1.
- modem:
- Less than zero: this is the negative of the network type,
and ttname is a network host name. Network types (from
ckcnet.h:
NET_TCPB 1 TCP/IP Berkeley (socket) (implemented in ckutio.c)
NET_TCPA 2 TCP/IP AT&T (streams) (not yet implemented)
NET_DEC 3 DECnet (not yet implemented)
Zero or greater: ttname is a terminal device name.
Zero means a direct connection (don't use modem signals).
Positive means use modem signals depending on the current setting
of ttcarr (see ttscarr()).
- timo:
- > 0: number of seconds to wait for open() to return before
timing out.
<=0: no timer, wait forever (e.g. for incoming call).
For real tty devices, ttopen() attempts to gain exclusive access to
the tty device, for example in UNIX by creating a "lockfile" (in other
operating systems, like VMS, exclusive access probably requires no special
action).
- Side effects:
- Copies its arguments and the tty file descriptor to global variables that
are available to the other tty-related functions, with the lcl value
altered as described above. Gets all parameters and settings associated
with the line and puts them in a global area, so that they can be restored
by ttres(), e.g. when the device is closed.
- Returns:
0: on success
-5: if device is in use
-4: if access to device is denied
-3: if access to lock mechanism denied
-2: upon timeout waiting for device to open
-1: on other error
- int
ttpkt(speed,flow,parity) long speed; int flow, parity;
- Puts the currently open tty device into the appropriate modes for
transmitting and receiving Kermit packets.
- Arguments:
- speed:
- if speed > -1, and the device is a true tty device, and Kermit is in
local mode, ttpkt also sets the speed.
- flow:
- if in the range 0-3, ttpkt selects the corresponding type of flow
control. Currently 0 is defined as no flow control, 1 is Xon/Xoff,
and no other types are defined. If (and this is a horrible hack, but
it goes back many years and will be hard to eradicate) flow is 4,
then the appropriate tty modes are set for modem dialing, a special
case in which we talk to a modem-controlled line without requiring
carrier. If flow is 5, then we require carrier.
- parity:
- This is simply copied into a global variable so that other
functions (like ttinl, ttinc, etc) can use it.
- Side effects:
- Copies its arguments to global variables, flushes the terminal
device input buffer.
- Returns:
-1: on error.
0: on success.
- int
ttsetflow(int)
- Enables the given type of flow control on the open serial communications
device immediately. Arguments are the FLO_xxx values from ckcdeb.h,
except FLO_DIAL, FLO_DIAX, or FLO_AUTO, which are not actual flow-control
types. Returns 0 on success, -1 on failure.
- #ifdef TTSPDLIST
- long *
ttspdlist()
- Returns a pointer to an array of longs, or NULL on failure. On success,
element 0 of the array contains number, n, indicating how many follow.
Elements 1-n are serial speeds, expressed in bits per second, that are
legal on this platform. The user interface may use this list to construct a
menu, keyword table, etc.
- #endif /* TTSPDLIST */
- int
ttres()
- Restores the tty device to the modes and settings that were in effect at
the time it was opened (see ttopen). Returns:
-1: on error.
0: on success.
- int
ttruncmd(string) char * string;
- Runs the given command on the local system, but redirects its input and
output to the communication (SET LINE, SET PORT, or SET HOST) device.
Returns:
0: on failure.
1: on success.
- int
ttscarr(carrier) int carrier;
- Copies its argument to a variable that is global to the other tty-related
functions, and then returns it. The values for carrier are defined in
ckcdeb.h: CAR_ON, CAR_OFF, CAR_AUTO. ttopen(), ttpkt(), and
ttvt() use this variable when deciding how to open the tty device and
what modes to select. The meanings are these:
CAR_OFF:
| Ignore carrier at all times.
|
CAR_ON:
| Require carrier at all times, except when dialing.
This means, for example, that ttopen() could hang forever waiting
for carrier if it is not present.
|
CAR_AUTO:
| If the modem type is zero (i.e. the connection is direct), this
is the same as CAR_OFF. If the modem type is positive, then heed
carrier during CONNECT (ttvt mode), but ignore it at other times
(packet mode, during SET LINE, etc). Compatible with pre-5A
versions of C-Kermit. This should be the default carrier mode.
|
Kermit's DIAL command ignores the carrier setting, but ttopen(),
ttvt(), and ttpkt() all honor the carrier option in effect
at the time they are called. None of this applies to remote mode (the tty
device is the job's controlling terminal) or to network host connections
(modem type is negative).
- int
ttsndb()
- Sends a BREAK signal on the tty device. On a real tty device, send a real
BREAK lasting approximately 275 milliseconds. If this is not possible,
simulate a BREAK by (for example) dropping down some very low baud rate,
like 50, and sending a bunch of null characters. On a network connection,
do the appropriate network protocol for BREAK. Returns:
-1: on error.
0: on success.
- int
ttsndlb()
- Like ttsndb(), but sends a "Long BREAK" (approx 1.5 seconds).
For network connections, it is identical to ttsndb(). Currently,
this function is used only if CK_LBRK is defined (as it is for UNIX and VMS).
- int
ttsspd(cps) int cps;
- For serial devices only, set the device transmission speed to (note
carefully) TEN TIMES the argument. The argument is in characters per
second, but transmission speeds are in bits per second. cps are used rather
than bps because high speeds like 38400 are not expressible in a 16-bit int
but longs cannot be used because keyword-table values are ints and not longs.
If the argument is 7, then the bps is 75, not 70. If the argument is 888,
this is a special code for 75/1200 split-speed operation (75 bps out, 1200
bps in). Returns:
-1: on error,
meaning the requested speed is not valid or available.
>=0: on success (don't try to use this value for anything).
- int
ttvt(speed,flow) long speed; int flow;
- Puts the currently open tty device into the appropriate modes for terminal
emulation. The arguments are interpreted as in ttpkt(). Side
effects: ttvt() stores its arguments in global variables, and sets a
flag that it has been called so that subsequent calls can be ignored so long
as the arguments are the same as in the last effective call. Other functions,
such as ttopen(), ttclose(), ttres(),
ttvt(), etc, that change the tty device in any way must unset this
flag. In UNIX Kermit, this flag is called tvtflg.
- int
ttwmdm(mdmsig,timo) int mdmsig, timo;
- Waits up to timo seconds for all of the given modem signals to appear.
mdmsig is a bit mask, in which a bit is on (1) or off (0) according to whether
the corresponding signal is to be waited for. These symbols are defined in
ckcdeb.h:
BM_CTS (bit 0) means wait for Clear To Send
BM_DSR (bit 1) means wait for Data Set Ready
BM_DCD (bit 2) means wait for Carrier Detect
Returns:
-3: Not implemented.
-2: This line does not have modem control.
-1: Timeout:
time limit exceeded before all signals were detected.
1: Success.
- int
ttxin(n,buf) int n; CHAR *buf;
- Reads x characters from the tty device into the specified buf,
stripping parity if parity is not none. This call waits forever, there is no
timeout. This function is designed to be called only when you know that at
least x characters are waiting to be read (as determined, for example,
by ttchk()). This function should use the same buffer as
ttinc().
- int
txbufr(timo) int timo;
- Reads characters into the internal communications input buffer. timo is a
timeout interval, in seconds. 0 means no timeout, wait forever. Called by
ttinc() (and possibly ttxin() and ttinl()) when the
communications input buffer is empty. The buffer should be called
ttxbuf[], its length is defined by the symbol TXBUFL. The global
variable txbufn is the number of characters available to be read from
ttxbuf[], and txbufp is the index of the next character to be read.
Should not be called if txbufn > 0, in which case the buffer does not need
refilling. This routine returns:
-2: Communications disconnect
-1: Timeout
>=0: A character (0 - 255)
On success, the first character that was read, with
the variables txbufn and txbufp set appropriately for any remaining
characters.
NOTE: Currently this routine is used internally only by the UNIX and VMS
versions. The aim is to make it available to all versions so there is one
single coherent and efficient way of reading from the communications device
or network.
- VOID
ztime(s) char **s;
- Returns a pointer, s, to the current date-and-time string in s. This
string must be in the fixed-field format associated with the C runtime
asctime() function, like: "Sun Sep 16 13:23:45 1973\n" so that
callers of this function can extract the different fields. The pointer value
is filled in by ztime, and the data it points to is not safe, so should be
copied to a safe place before use. ztime() has no return value. As
a side effect, this routine can also fill in the following two external
variables (which must be defined in the system-dependent modules for each
platform):
long ztusec:
Fraction of seconds of clock time, microseconds.
long ztmsec:
Fraction of seconds of clock time, milliseconds.
If these variables are not set by zstime(), they remain at their
initial value of -1L.
- int
gtimer()
- Returns the current value of the elapsed time counter in seconds (see
rtimer), or 0 on any kind of error.
-
#ifdef GFTIMER
CKFLOAT
gftimer()
- Returns the current value of the elapsed time counter in seconds, as
a floating point number, capable of representing not only whole seconds,
but also the fractional part, to the millisecond or microsecond level,
whatever precision is available. Requires a function to get times at
subsecond precision, as well as floating-point support. That's why it's
#ifdef'd.
- #endif /* GFTIMER */
- int
msleep(m) int m;
- Sleeps (pauses, does nothing) for m milliseconds (a millisecond is one
thousandth of a second). Returns:
-1: on failure.
0: on success.
- VOID
rtimer()
- Sets the elapsed time counter to zero. If you want to time how long an
operation takes, call rtimer() when it starts and gtimer when it ends.
rtimer() has no return value.
- #ifdef GFTIMER
VOID
rftimer()
- Sets the elapsed time counter to zero. If you want to time how long an
operation takes, call rftimer() when it starts and gftimer when it
ends. rftimer() has no return value. Note: rftimer() is to
be used with gftimer() and rtimer() is to be used with
gtimer(). See the rftimer() description.
- #endif /* GFTIMER */
- int
sysinit()
- Does whatever needs doing upon program start. In particular, if the
program is running in any kind of privileged mode, turns off the privileges
(see priv_ini()). Returns:
-1: on error.
0: on success.
- int
syscleanup()
- Does whatever needs doing upon program exit. Returns:
-1: on error.
0: on success.
- int
psuspend()
- Suspends the Kermit process, puts it in the background so it can be
continued ("foregrounded") later. Returns:
-1: if this function is not supported.
0: on success.
[ Contents ]
[ C-Kermit ]
[ Kermit Home ]
As of version 5A, C-Kermit includes support for several networks. Originally,
this was just worked into the
ttopen(),
ttclos(),
ttinc(),
ttinl(), and similar routines in
ckutio.c.
But this made it impossible to share this code with non-UNIX versions, like
VMS, AOS/VS, OS/2, etc. So as of edit 168, network code has been separated
out into its own module and header file,
ckcnet.c and
ckcnet.h:
ckcnet.h: Network-related symbol definitions.
ckcnet.c: Network i/o (TCP/IP, X.25, etc), shared by most platforms.
cklnet.c: Network i/o (TCP/IP, X.25, etc) specific to Stratus VOS.
The routines and variables in these modules fall into two categories:
- Support for specific network packages like SunLink X.25 and TGV MultiNet,
and:
- support for specific network virtual terminal protocols like CCITT X.3 and
TCP/IP Telnet.
Category (1) functions are analogs to the tt*() functions, and have
names like netopen, netclos, nettinc, etc. Group A-D modules do not (and must
not) know anything about these functions -- they continue to call the old
Group E functions (ttopen, ttinc, etc). Category (2) functions are protocol
specific and have names prefixed by a protocol identifier, like tn for telnet
x25 for X.25.
ckcnet.h contains prototypes for all these functions, as well as
symbol definitions for network types, protocols, and network- and protocol-
specific symbols, as well as #includes for the header files necessary
for each network and protocol.
The following functions are to be provided for networks that do not use normal
system i/o (open, read, write, close):
- int
netopen()
- To be called from within ttopen() when a network connection is
requested. Calling conventions and purpose same as Group E
ttopen().
- int
netclos()
- To be called from within ttclos() when a network connection is
being closed. Calling conventions and purpose same as Group E
ttclos().
- int
nettchk()
- To be called from within ttchk().
Calling conventions and purpose same as Group E ttchk().
- int
netflui()
- To be called from within ttflui().
Calling conventions and purpose same as Group E ttflui().
- int
netbreak()
- To send a network break (attention) signal.
Calling conventions and purpose same as Group E ttsndbrk().
- int
netinc()
- To get a character from the network.
Calling conventions same as Group E ttsndbrk().
- int
nettoc()
- Send a "character" (byte) to the network.
Calling conventions same as Group E ttoc().
- int
nettol()
- Send a "line" (sequence of bytes) to the network.
Calling conventions same as Group E ttol().
Conceivably, some systems support network connections simply by letting you
open a device of a certain name and letting you do i/o to it. Others (like
the Berkeley sockets TCP/IP library on UNIX) require you to open the
connection in a special way, but then do normal i/o (read, write). In such a
case, you would use netopen(), but you would not use nettinc, nettoc,
etc.
VMS TCP/IP products have their own set of functions for all network
operations, so in that case the full range of netxxx()
functions is used.
The technique is to put a test in each corresponding ttxxx()
function to see if a network connection is active (or is being requested),
test for which kind of network it is, and if necessary route the call to the
corresponding netxxx() function. The netxxx()
function must also contain code to test for the network type, which is
available via the global variable ttnet.
[ Contents ]
[ C-Kermit ]
[ Kermit Home ]
4.F.1. Telnet Protocol
(This section needs a great deal of updating...)
As of edit 195, Telnet protocol is split out into its own files, since it
can be implemented in remote mode, which does not have a network connection:
ckctel.h:
Telnet protocol symbol definitions.
ckctel.c:
Telnet protocol.
The Telnet protocol is supported by the following variables and routines:
- int tn_init
- Nonzero if telnet protocol initialized, zero otherwise.
- int
tn_init()
- Initialize the telnet protocol (send initial options).
- int
tn_sopt()
- Send a telnet option.
- int
tn_doop()
- Receive and act on a telnet option from the remote.
- int
tn_sttyp()
- Send terminal type using telnet protocol.
4.F.2. FTP Protocol
(To be filled in...) See the source file
4.F.3. HTTP Protocol
(To be filled in...)
4.F.4. X.25 Networks
These routines were written for SunLink X.25 and have since been adapted to at
least on one other: IBM AIXLink/X.25.
- int
x25diag()
- Reads and prints X.25 diagnostics
- int
x25oobh()
- X.25 out of band signal handler
- int
x25intr()
- Sends X.25 interrupt packet
- int
x25reset()
- Resets X.25 virtual circuit
- int
x25clear()
- Clear X.25 virtual circuit
- int
x25stat()
- X.25 status
- int
setqbit()
- Sets X.25 Q-bit
- int
resetqbit()
- Resets X.25 Q-bit
- int
x25xin()
- Reads n characters from X.25 circuit.
- int
x25inl()
- Read a Kermit packet from X.25 circuit.
[ Contents ]
[ C-Kermit ]
[ Kermit Home ]
4.F.5. Adding New Network Types
Example: Adding support for IBM X.25 and Hewlett Packard X.25.
First, add new network type symbols for each one. There are already
some network types defined for other X.25 packages:
NET_SX25 is the network-type ID for SunLink X.25.
NET_VX25 is the network-type ID for VOS X.25.
So first you should new symbols for the new network types, giving them
the next numbers in the sequence, e.g.:
#define NET_HX25 11 /* Hewlett-Packard X.25 */
#define NET_IX25 12 /* IBM X.25 */
This is in ckcnet.h.
Then we need symbols to say that we are actually compiling in the code
for these platforms. These would be defined on the cc command line:
-DIBMX25 (for IBM)
-DHPX25 (for HP)
So we can build C-Kermit versions for AIX and HP-UX both with and without
X.25 support (since not all AIX and IBM systems have the needed libraries,
and so an executable that was linked with them might no load).
Then in ckcnet.h:
#ifdef IBMX25
#define ANYX25
#endif /* IBMX25 */
#ifdef HPX25
#define ANYX25
#endif /* HPX25 */
And then use ANYX25 for code that is common to all of them,
and IBMX25 or HPX25 for code specific to IBM or HP.
It might also happen that some code can be shared between two or more of
these, but not the others. Suppose, for example, that you write code that
applies to both IBM and HP, but not Sun or VOS X.25. Then you add the
following definition to ckcnet.h:
#ifndef HPORIBMX25
#ifdef HPX25
#define HPORIBMX25
#else
#ifdef IBMX25
#define HPORIBMX25
#endif /* IBMX25 */
#endif /* HPX25 */
#endif /* HPORIBMX25 */
You can NOT use constructions like "#if defined
(HPX25 || IBMX25)"; they
are not portable.
[ Contents ]
[ C-Kermit ]
[ Kermit Home ]
So far, this is used only for the fullscreen local-mode file transfer display.
In the future, it might be extended to other uses. The fullscreen display
code is in and around the routine
screenc() in
ckuusx.c.
In the UNIX version, we use the curses library, plus one call from the termcap
library. In other versions (OS/2, VMS, etc) we insert dummy routines that
have the same names as curses routines. So far, there are two methods for
simulating curses routines:
- In VMS, we use the Screen Management Library (SMG), and insert stubs
to convert curses calls into SMG calls.
- In OS/2, we use the MYCURSES code, in which the stub routines
actually emit the appropriate escape sequences themselves.
Here are the stub routines:
- int
tgetent(char *buf, char *term)
- Arguments are ignored. Returns 1 if the user has a supported terminal
type, 0 otherwise. Sets a global variable (for example, "isvt52" or
"isdasher") to indicate the terminal type.
- VOID
move(int row, int col)
- Sends the escape sequence to position the cursor at the indicated row
and column. The numbers are 0-based, e.g. the home position is 0,0.
- int
clear()
- Sends the escape sequence to clear the screen.
- int
clrtoeol()
- Sends the escape sequence to clear from the current cursor position to
the end of the line.
In the MYCURSES case, code must be added to each of the last three routines
to emit the appropriate escape sequences for a new terminal type.
- clearok(curscr), wrefresh()
- In real curses, these two calls are required to refresh the screen, for
example after it was fractured by a broadcast message. These are useful
only if the underlying screen management service keeps a copy of the entire
screen, as curses and SMG do. C-Kermit does not do this itself.
[ Contents ]
[ C-Kermit ]
[ Kermit Home ]
(To be filled in...) But see:
these comments, and the source files
ckupty.h
and
ckupty.c.
(To be filled in...) Meanwhile, see
security.html.
[ Contents ]
[ C-Kermit ]
[ Kermit Home ]
The format of this field (the "," attribute) is interpreted according to the
System ID ("." Attribute).
For UNIX (System ID = U1), it's the familiar 3-digit octal number, the
low-order 9 bits of the filemode: Owner, Group, World, e.g. 660 = read/write
access for owner and group, none for world, recorded as a 3-digit octal string.
High-order UNIX permission bits are not transmitted.
For VMS (System ID = D7), it's a 4-digit hex string, representing the 16-bit
file protection WGOS fields (World,Group,Owner,System), in that order (which
is the reverse of how they're shown in a directory listing); in each field,
Bit 0 = Read, 1 = Write, 2 = Execute, 3 = Delete. A bit value of 0 means
permission is granted, 1 means permission is denied. Sample:
r-01-00-^A/!FWERMIT.EXE'"
s-01-00-^AE!Y/amd/watsun/w/fdc/new/wermit.exe.DV
r-02-01-^A]"A."D7""B8#119980101 18:14:05!#8531&872960,$A20B-!7(#512@ #.Y
s-02-01-^A%"Y.5!
A VMS directory listing shows the file's protection as (E,RWED,RED,RE) which
really means (S=E,O=RWED,G=RED,W=RE), which is reverse order from the internal
storage, so (RE,RED,RWED,E). Now translate each letter to its corresponding
bit:
RE=0101, RED=1101, RWED=1111, E=0010
Now reverse the bits:
RE=1010, RED=0010, RWED=0000, E=1101
This gives the 16-bit quantity:
1010001000001101
This is the internal representation of the VMS file permission; in hex:
A20B
as shown in the sample packet above.
The VMS format probably would also apply to RSX or any other FILES-11 system.
To be used when the two systems are different (and/or do not recognize or
understand each other's local protection codes).
First of all, the book is wrong. This should not be the World protection,
but the Owner protection. The other fields should be set according to system
defaults (e.g. UNIX umask, VMS default protection, etc), except that no
non-Owner field should give more permissions than the Owner field.
[ Top ]
[ Contents ]
[ C-Kermit Home ]
[ Kermit Home ]
C-Kermit Program Logic Manual /
The Kermit Project /
kermit@kermitproject.org /
27 September 2011