home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
C!T ROM 2
/
ctrom_ii_b.zip
/
ctrom_ii_b
/
PROGRAM
/
C
/
MBLIB10
/
MB_LIB.DOC
< prev
next >
Wrap
Text File
|
1993-03-07
|
122KB
|
3,157 lines
=============================================================
MB_lib V1.0
Hudson and Opus message base interface library for C
(c) F.W. van Wensveen 1992, 1993
All Rights Reserved
=============================================================
The author of this software can be reached via E-mail
as Frank Van.wensveen at the following addresses:
2:285/504@fidonet.org
27:1331/703@signet.org
310/902 GT Power net
frank.van.wensveen@p0.f504.n285.z2.fidonet.org
BBS: +31-10-4717477
(V21, V22, V22b, V23, V32b, V42b, MNP2..5 - 24h/day
Abstract
MB_lib is a message library for use with C (calling the
routines from other languages is, of course, also
possible). It provides a full set of services needed to
interface your applications with both Hudson and *.MSG-
style message bases quite easily.
This document only contains flat ASCII.
============================================================
1. INTRODUCTION
============================================================
Why MB_lib?
-----------
One of the most important features BBSes and mail systems is the
ability to handle electronic mail. This enables people from all
over the world to communicate with each other against hardly more
than the cost of (usually) local phone calls. Initially, messages
were stored by the BBS and/or mailer software in separate files
for each message (the well-known *.MSG files).
Since this proved a very inefficient way of using disk space, the
Hudson message base was developed. This message base stores all
messages in only five files, one of which contains the total text
of all messages in the system, while the other files act as index
files. This system uses the available more efficiently. Today,
the Hudson message base is used by many popular BBS and mail
programs like Remote Access, SuperBBS, Quick BBS, FrontDoor,
TosScan, Fmail, and lots, lots more.
The Hudson message base however has the disadvantage that it is
quite complicated to access when compared with a simple *.MSG
file. Therefore, programmers have had more difficulty developing
software to interface with the Hudson message base, since they
would have to write their own, often complicated, interface
routines first, before getting down to the meat of their
applications.
This library intends to remedy that by providing a full set of
functions that allow you to interface with both Hudson and *.MSG
message bases in a very simple way. If you're a C programmer,
this is what you've been waiting for.
MB_lib is a library intended for use with C. It was written using
Borland Turbo C 2.01. and Turbo Assembler 1.5. It may also work
with other compilers than Borland's. Of course you can also call
the functions from other languages like Assembly, provided you
know how. I intend to release a version for Borland C++ for
Windows, but at the time this document was written, this version
isn't finished yet. As far as I know this is the first library
offering these services to the C community.
A WORD TO THOSE WHO'D RATHER USE PASCAL
---------------------------------------
For those with Pascal experience the story might begin to sound
familiar. For Turbo Pascal there already is a unit with message
base routines available. This is MSGBASE by Richard Faasen of
Sliedrecht, The Netherlands, SysOp of InFase BBS (+31-1840-
21818). So if you're a Pascal programmer, you can either switch
to C (why not?) or contact Richard.
This library is based upon Richards original Pascal source code.
(See also the credits at the end of this document.)
2
FEATURES
--------
The most important features of MB_lib include (but are not
limited to):
* All you need to read, write, search for or kill messages.
Writing a new message is as easy as msg_write_new ()!
* Dynamic use of memory. Message text can have *any* size,
the only restriction being actual memory limits. Text is
manipulated using a 'text handle'.
* Flexible, well-behaved, and stable. With flexible I mean
that MB_lib won't get in the way of whatever you're doing in
your applications (you can, for example, call MB_lib
routines when in graphics mode without problems). MB_lib
interfaces with your operating system in a decent way.
* Powerful yet flexible error trapping and -handling.
* Fully supports the Remote Access 1.11 message base locking
technique. This means you can write programs suited for
multi-tasking environments, as required by applications for
Remote Access on multi-line systems.
* Written with portability in mind. I haven't tried it as yet,
but porting MB_lib to other platforms than MS-DOS should be
relatively easy. The only system-dependent code is in the
locking mechanism, which other OSes might not need.
* No-nonsense programming style. No effort has been made to
dress up the software or to provide flashy effects. Instead,
the code is flexible and well-behaved enough so that you may
do that yourself if you wish. MB_lib concentrates on
providing fast, stable and efficient services without
unwanted overhead (Unix people will get the idea).
FILES IN THE PACKAGE
--------------------
The distribution package of MB_lib should contain the following
files:
MB_LIB.DOC The file you are currently reading
MB_LIB.H The header file for MB_lib
MB_LIBT.LIB Message base library file, Tiny model
MB_LIBS.LIB Message base library file, Small model
MB_LIBC.LIB Message base library file, Compact model
MB_LIBM.LIB Message base library file, Medium model
MB_LIBL.LIB Message base library file, Large model
MB_LIBH.LIB Message base library file, Huge model
*.C Example programs. Look here for tips & tricks on
how to use the library functions in your programs.
RALCK003.DOC The Remote Access Locking Specification version 3,
contains details on message base locking.
If you receive a version with any of these files missing, you can
obtain a complete and uncorrupted version of MB_lib from the
author.
3
ABOUT THIS MANUAL
-----------------
Like the code, this manual is very straightforward. It provides
brief insights on how the Hudson message base stores messages,
how MB_lib handles things, and than proceeds to describe the
library functions. In other words, this document is meant for
people familiar with C. It is beyond the scope of this text to
explain how functions are called or how pointers are used to
handle data.
The function descriptions should look familiar to anyone who has
ever seen a C reference guide or used a man command on a Unix
system. Basic knowledge about echomail is assumed. It wouldn't
hurt to be familiar with FTS-0001, either.
Sometimes examples are given in the reference section. Sometimes
these examples are complete programs, sometimes not. The only
purpose of these examples is to illustrate how a certain function
works, so do not regard them as complete and functional programs.
Example source code is included in the distribution package. It
might really be a good idea to have a look at it.
CREDITS
-------
Special thanks to the following people:
* Richard Faasen, who wrote the original Pascal version
(MsgBase 1.0) and gave me the Pascal source to have a go at
it in C. Never mind the fact that he tried to convert me to
C++ just before the beta version was finished. <grin>
* The beta team: Feico de Boer, Olaf Westrik, Arnoud de Jonge,
Richard Faasen, plus lots of people who made suggestions,
offered advice, or just gave moral support.
* John Lots for many tips and pieces of advice that (sometimes
unintended and without his knowledge) finally ended up here.
* Brian Pirie of Ottawa, Ontario, who knows how to write a
hashing algorithm.
* Everyone who replied to my questions in the FIDOnet C and
Assembly conferences.
Thank you, guys! You were all most helpful.
DISTRIBUTION, LICENCING AND LEGALESE
------------------------------------
The latest version of MB_lib is always available on my BBS, or
you can request it from one of my E-mail addresses. See the front
page of this document for details. For support, bug reports,
etc., you can also contact me at the adresses on the front page.
If you've written programs using this library, I'd really like to
see them, and, if you want me to, I'll be happy to comment on
them, and distribute the via my BBS.
You may (and in fact, are encourage to) copy this software and
distribute those copies. However, YOU MAY NOT:
4
- Alter or remove any portion of the package, including this
document, in any way.
- Distribute it on a commercial basis. You may, however,
charge a nominal fee not to exceed the cost of floppy disks
and mailing costs.
- Distribute program's you've written and compiled an
unregistered version of MB_lib.
Disclaimer: The author of this software does not accept any
responsibility for any damage, direct or indirect, including, but
not limited to, lost profits, lost savings, or loss or corruption
of data, resulting from the use of, or the inability to use, this
software.
The software is provided 'as is', without any guarantee whatsoe-
ver. If it works for you, fine. If it does not, too bad. If you
try this program and your system goes to pieces, I'm not obliged
to provide you with a tube of glue. It is the users responsibili-
ty to make backups of important data, to carefully check out
untested software, and to take precautions in general. So if you
like to live dangerously, on your own head be it.
The author retains the copyright to this software. This software
is NOT in the public domain. Source code is not available, and,
by previous agreement to other parties, it won't become available
in the future.
As to licencing: THIS IS NOT FREE SOFTWARE! I know, for many
people this part is a giggle, but bear with me for a moment.
You may try this software for 30 days. If you decide not to
continue using it after the trial period of 30 days, you must
stop using it, and you must destroy (== delete) all executables
containing linked code from MB_lib.
If you decide that the stuff is worth the disk space, you must
register MB_lib. See the file REGISTER.TXT, included in this
package, for conditions and full details on how to register.
A note about the shareware concept
----------------------------------
Just to make your conscience uneasy, consider this. In the past
I've written stuff on a write-it-when-I-need-it basis. Sometimes,
when a piece of software came in particularly handy, I decided to
distribute it. I granted permission to anyone to use the software
absolutely free, but I asked to be sent a netmail message, a
postcard, or whatever, when someone decided to use my stuff.
Since then, I've seen my programs popping up in the most unlikely
places. So people actually use it, which gives me a real kick.
But the number of reactions I got was very disappointing. This
may be due to the fact that I never crippled distribution
versions. This sounds ridiculous, and, if true, it is.
So, when people will use my software (in which, by the way, I've
invested a considerable amount of time), but consider it too much
trouble to even drop me a netmail message, this may very well be
the last program from me. This time it's really going to depend
on user response. If I don't get any - again - then I won't spend
too much time at the keyboard next time. On the other hand, any-
and I mean *any* - reaction is an impulse for me to continue
5
with projects like these. I'm sorry to put it this bluntly, but
that's the way it is. It's all in your hands now.
If you don't use the stuff, fine. But at least drop me a line to
let me know what you think of it. If you're experimenting with
this, the odds are that you know how to send me a netmail
message. If not, ask the nearest SysOp, he'll be more than happy
to instruct you.
If, on the other hand, you decide to use it, even better! But
register the stuff. It's cheap, and it will keep me at the
keyboard to write something even better.
OK - some have hinted that I didn't get any reactions because my
documentation intro's tend to get long-winded... so let's get on
with it!
6
============================================================
2. TECHNICAL BACKGROUND INFORMATION
============================================================
A BRIEF OVERVIEW OF THE HUDSON MESSAGE BASE
-------------------------------------------
It is beyond the scope of this text to explain the Hudson message
base in detail. For those who want to know more, see the original
specifications, if you can find 'em.
The Hudson message base consists of five files. Each file
consists of one or more records, the format of which you can find
in the MB_lib header file. A short description follows:
MSGINFO.BBS This file is the only one with a fixed size.
It contains a single record specifying info
regarding the entire message base: the number
of messages in the base, the lowest and
highest number, and the amount of messages in
each board.
MSGIDX.BBS Index file. This file contains a record for
each message in the base. This record
specifies the message number and the board it
can be found in. This file can be used for
searching for messages in a certain board.
MSGTOIDX.BBS Index file. This file contains a record for
each message in the base. The record contains
the who_to field from the corresponding
message header, if the message hasn't been
received by the user yet. (Mail readers
should overwrite this record when a message
has been read.) This file can be used for
searching for unread messages to a certain
user.
MSGHDR.BBS Index file. This file contains a record for
each message in the base. The records contain
the header of the corresponding message. Two
fields in this header specify the number of
the first text block of the message, and the
number of text blocks.
MSGTEXT.BBS Ah, this is the one. This file contains
blocks of text, that make up the actual
message text body. Start en length of
messages are specified by fields in the
message header.
The files marked as 'Index file' have indeed an index function.
That means that record n in one index file corresponds with
record n in another index file. In other words, if a search for
unread mail in MSGTOIDX.BBS results in a search hit at record
100, you should examine record 100 in MSGIDX.BBS to find the
message number and board number, read record 100 in MSGHDR.BBS to
get the message header, and examine this header to determine
which records to read from MSGTEXT.BBS to dig up the message
text. But you needn't worry about this - MB_lib will do that for
you.
7
Obviously, if one of the index files should be corrupted, this
could mean loss of all messages in the base. Panic!
Well, it's not as bad as that. If the files MSGHDR.BBS and
MSGTXT.BBS are uncorrupted, the other files can be regenerated
from these by many message base maintenance utilities.
INTRODUCTION TO FILE LOCKING
----------------------------
In an multi-tasking environment where two or more processes are
sharing a single database or file, steps must be taken to prevent
file or database corruption when writing.
Picture this: we read a message header, update the reply link
pointers, and write it back. Nothing wrong so far. Now try this:
we read a file header. While we're modifying the reply link
pointers, another process (like another instance of the BBS
program handling another line) also reads the header. We write
our modified header back, after which the other process does the
same, THEREBY OVERWRITING THE MODIFICATIONS WE JUST MADE. Voila:
one corrupt message base.
To prevent all this, we use file locking. Before modifying one of
the files, we lock the message base. That is: we indicate then no
other process may write to the files at this moment. Then we
read and modify our records, write them back, and proceed to
unlock the message base. Other operating systems than MS-DOS
(Unix for example) handle this for us, but if we're using MS-DOS,
we must do it ourselves. Thanks a bundle, MicroSoft.
MB_lib meets the official Remote Access Locking Specification.
That means that we don't use file locking in the normal sense,
relying on SHARE to provide the services that plain DOS doesn't
offer. Instead, the files are locked outside their boundaries,
while RA checks for a lock just there. In the meantime, a 0-byte
file in RA's semaphore file directory can indicate that the
message base must be unlocked NOW. So, as I said, it's not real
record locking in the normal (OS) sense. See the RA locking
specification for details.
Should you use file locking when writing a program? YES!!! You
may not be running a multi-line BBS, but somebody else may, so
that your program would corrupt the message base of that system
if you didn't use file locking.
MB_lib provides two functions for easy locking and unlocking the
message base, so that you won't have much trouble handling this
properly.
Since other processes can't write while we've got the message
base locked, we should unlock it regularly. When performing
several write operations at a time, for example while changing an
entire message, you should use the following locking / unlocking
sequences:
Changing a message header:
- Lock the message base
- Read the message header
- Modify it
- Write it back
- Unlock the message base
Writing new message text when the message header exists:
- Create message text
- Lock the message base
8
- Read the header
- Write message text
- Modify header
- Write back header
- Unlock message base
The same goes for *any* write operation (create, modify, kill,
whatever) to the message base. Lock the message base, do your
modification, unlock, lock, do another modification, unlock.
Always lock as short as possible. If you should need to lock the
message base for more than 15 seconds at a time, you must take
some special precautions. See the document RALCK003, enclosed in
this package, for details.
HOW TEXT IS STORED IN MEMORY
----------------------------
The size of a message text body is unknown. The Hudson message
base allows - in theory - for a maximum text size of 16 Mb,
though many BBS and mail processor packages can't handle more
than 8 or 16 kb. Therefore it is wise not to write messages
larger than 8 kb.
Anyway, we can't allocate a fixed data space for a message text.
Therefore, text space is dynamically allocated.
Text is manipulated with a so-called text handle. For the
technically inclined: this text handle is actually a pointer to
the beginning of a linked list of records, consisting of pointers
to text blocks and pointers to the next record in the chain.
Text handles are variables of type M_TEXT. You declare a text
handle as follows:
M_TEXT msgtxt; /* Text handle to manipulate message text */
That's all! Text handles are returned by functions like the
msg_read_text () and txt_new () functions. For example:
msgtxt = msg_read_text (& header); /* Read message text */
To dump this text to, say, the screen, you use the same text
handle:
txt_dump (msgtxt, printline, margin, NOKLUDGES);
It's a simple as that!
HOW MB_LIB HANDLES ERROR TRAPPING
---------------------------------
This may be news to some programmers... <grin> but many things
can go wrong while a program is running, and it is wise to check
for errors, so that when (not if) an error occurs, the program
can gracefully exit. Many programs, especially those written in
Pascal for some reason, lack airtight error trapping, so that
they sometimes exit with the highly informative message RUNTIME
ERROR AT 1234:ABCD (or some other address). Anyway, I've labored
to make error trapping in MB_lib as foolproof as possible.
9
Since library code never should get in the way of whatever the
application is doing, error trapping is very flexible. Library
routines do not exit to the OS. Library codes do not print error
messages. Nor do they generate beeps or anything else. The
application programmer can do all this if desired.
Every library function that can encounter an error returns a
completion code. This code usually is zero to indicate error-free
completion, or non-zero if an error was encountered. When
functions are supposed to return a number (like a message or
record number), the error completion code is -1, otherwise it is
an integer indication the nature of the error. For example:
if (msg_open ("C:\\MSGBASE"))
error (); /* Invoke programmer-supplied error handler */
The programmer has two global variables at his/her disposal, to
determine the nature of the error. When an error occurs, these
variables are automatically set to indicate what went wrong, and
can be used in an error handler.
First, there is the int errortype variable. This variable can
have the following values:
NOT_ERR (0x00) /* No error */
MEM_ERR (0x01) /* Memory error - out of memory */
FRD_ERR (0x02) /* File read error */
FWR_ERR (0x03) /* File write error */
FCR_ERR (0x04) /* File create error */
MNO_ERR (0x05) /* Message base not open error */
IRN_ERR (0x06) /* Invalid record no., out of bound */
MBC_ERR (0x07) /* Msg base corrupt */
THIS VALUE ALSO APPLIES TO THE FUNCTION COMPLETION CODES!
Examining either the completion code or the errortype variable
can be used to trap errors.
Then there is the char errorstring [] variable. This string is
filled with a message telling the user what went wrong, in
readable text.
Using these two variables, an error handler could look like this:
void error_exit (void) {
printf ("%cError %d accessing message base:\n%s\n",
BELL, errortype, errorstring);
exit (1);
}
Because MB_lib routines do not exit to the OS or print anything,
you won't get problems when, for example, an error occurs while
in graphics mode. On the other hand, if you always remember to
check completion codes, you will be able to handle any kind of
error, critical or non-critical.
USE OF THE REGISTRATION KEY
---------------------------
Users who register their copy of MB_lib will receive a registra-
tion key. This key is a small file, that should be included in
10
the program at the top of the source listing. Typically you would
add a include "mb_lib.key" statement to your program along
with the include statements for, say, the stdio.h file.
That is all! The registration key does not take up extra
code/data space in the compiled executables. The only thing you
will notice is that the compiled program no longer displays the
'unregistered' message when running.
With the key, you can recompile your programs for distribution.
HOW MB_LIB IS DIVIDED INTO SEGMENTS
-----------------------------------
Because the whole library is a large chunk of code, it would be
difficult to handle at once. Therefore, the library is divided
into segments. This provides a reasonable compromise between
efficiency and code size. If a segment isn't used, it is not
linked to your executable.
The functions in each segment are discussed in a separate
chapter. The segments have the following functions:
TEXT Functions to handle text
ACCESS Functions to access the message base
LOCK Functions to take care of message base locking to
enable your programs to run in a multi-tasking or
multi-line BBS environment
MANIP Message manipulation functions
READ Functions to read records and messages
WRITE Functions to write records and messages
SEARCH Functions to search for messages
NETMAIL This segment contains all functions to handle messages
in the *.MSG format (like netmail messages with most
mailers).
11
============================================================
3. TYPES, VARIABLES AND DEFINES
============================================================
=======================
M_TEXT
=======================
Name M_TEXT - text handle type
Usage M_TEXT <handlename>
Description As explained above, a text handle is used to
manipulate message text. A variable of this type
is returned by functions like txt_new ().
See also The background information given above, and the
reference section on the txt_new () (and other)
functions.
Example M_TEXT msgtext; /* Text handle */
=======================
msginfo
=======================
Name msginfo - a global MSGINFO.BBS record for various
purposes
Usage MSGINFO_RECORD msginfo;
Description Since MSGINFO.BBS contains only one record, which
can have only one value, it makes sense to define
one global record in which this information can
be stored. Instead of reading the file over and
over again to a locally defined record, this
variable provides one record which can be referred
to when MSGINFO.BBS data is needed for any
purpose.
See also msg_read_info ();
========================
errortype
========================
Name errortype - indicate what went wrong
Usage int errortype;
Description The errortype variable is set when MB_lib
encounters an error. It can have the following
values:
NOT_ERR No error
MEM_ERR Memory error (out of memory)
12
FRD_ERR File read error
FWR_ERR File write error
FCR_ERR File creation error
MNO_ERR Message base not opened before
accessing it
IRN_ERR Invalid record number specified for
read or write, attempt to access
files outside boundaries
MBC_ERR Message base corrupt - header
specifies non-existent records
Remarks These values also apply to the completion codes
returned by the functions which do not return a
record or message number. It is set when *any*
function encounters an error. Checking it is up to
you. However, errortype may have been set by a
previous function after which you didn't check. In
general, using completion codes to trap errors is
the most reliable way.
See also errorstring
Example See errorstring
========================
errorstring
========================
Name errorstring - tell the user what went wrong
Usage char errorstring []
Description The errorstring variable is set whenever the
errortype variable is set. It contains a string
telling you in readable text what went wrong. The
string is not \n terminated.
Remarks See errortype
See also errortype
Example void errorexit (void) { /* Exit with error */
putchar (BELL);
printf ("MB_lib error %d: %s\n", errortype,
errorstring);
exit (1);
}
=======================
Various defines
=======================
For your convenience, the following values have been defined in
MB_lib.h:
13
HCR Hard carriage return to indicate the end of
a paragraph in messages
SCR Soft carriage return, may be reformatted by
message readers
LF Line feed
KLUDGE The kludge delimiter. If this is the first
character on a line, the rest of that line up
to the first HCR will be used as a kludge.
BELL The bell character
KLUDGES Indicates that the txt_dump () function
should dump kludges
NOKLUDGES Indicates that the txt_dump () function
should not dump kludges. See txt_dump () for
details.
=======================
Message attributes
=======================
#define MA_DELETED 0x01 /* Message attributes */
#define MA_UNSENT 0X02
#define MA_NETMAIL 0X04
#define MA_PRIVATE 0X08
#define MA_RECEIVED 0X10
#define MA_UNMOVED 0X20
#define MA_LOCAL 0X40
#define NA_KILL 0X01 /* Hudson netmail attributes */
#define NA_SENT 0X02
#define NA_FILE 0X04
#define NA_CRASH 0X08
#define NA_RECEIPT 0X10
#define NA_AUDIT 0X20
#define NA_RETURN 0X40
#define NM_PRIVATE 0X0001 /* Opus (*.MSG) Netmail attributes */
#define NM_CRASH 0x0002
#define NM_RECEIVED 0x0004
#define NM_SENT 0x0008
#define NM_FILE 0x0010
#define NM_TRANSIT 0x0020
#define NM_ORPHAN 0x0040
#define NM_KILL 0x0080
#define NM_LOCAL 0x0100
#define NM_HOLD 0x0200
#define NM_UNUSED 0x4000
#define NM_REQUEST 0x0800
#define NM_RECEIPT 0x1000
#define NM_ISRECEIPT 0x2000
#define NM_AUDIT 0x4000
#define NM_UPDATEREQ 0x8000
14
=======================
Record structures
=======================
The following structures make up the message base file records. BEWARE!
Since the strings in the Hudson message base are Pascal-format strings,
you should NEVER read or write message base records yourself, but
instead use the functions provided in MB_lib, so that the proper
conversion can be performed.
These structures all use C-style strings - you needn't worry about
formats here. Just remember to use the MB_lib functions for message
base I/O. That's what they're for, anyway, so why would you want to do
otherwise?
typedef struct { /* MSGINFO.BBS structure definition */
unsigned int low_msg; /* Lowest msg # in message base */
unsigned int high_msg; /* Highest msg # in message base */
unsigned int total_msgs; /* Total # of messages in base */
unsigned int total_on_board [200]; /* Number of msgs / board */
} MSGINFO_RECORD;
typedef struct { /* MSGIDX.BBS structure definition */
int msg_num; /* Message # */
unsigned char board; /* Board # where msg is stored */
} MSGIDX_RECORD;
typedef char MSGTOIDX_RECORD [36]; /* MSGTOIDX.BBS structure def. */
typedef struct { /* MSGHDR.BBS structure definition */
int msgnum; /* Message number */
unsigned int prev_reply; /* Msg # of previous reply, 0 if no */
unsigned int next_reply; /* Msg # of next reply, 0 if none */
unsigned int times_read; /* # of times msg was read, UNUSED */
unsigned int start_block; /* Record # of msg in MSGTXT.BBS */
unsigned int num_blocks; /* # of records in MSGTXT.BBS */
unsigned int dest_net; /* Destination net */
unsigned int dest_node; /* Destination node */
unsigned int orig_net; /* Origin net */
unsigned int orig_node; /* Origin node */
unsigned char dest_zone; /* Destination zone */
unsigned char orig_zone; /* Origin zone */
unsigned int cost; /* Cost (Netmail) */
unsigned char msg_attr; /* Msg attributes. Bits as follows: */
/* 0 : Deleted */
/* 1 : Unsent */
/* 2 : Netmail */
/* 3 : Private */
/* 4 : Received */
/* 5 : Unmoved outgoing echo */
/* 6 : Local */
/* 7 : RESERVED */
unsigned char net_attr; /* Netmail attributes. Bits follow: */
/* 0 : Kill/Sent */
/* 1 : Sent */
/* 2 : File attach */
/* 3 : Crash */
/* 4 : Receipt request */
/* 5 : Audit request */
15
/* 6 : Is a return receipt */
/* 7 : RESERVED */
unsigned char board; /* Message board # */
char post_time [6]; /* Time message was posted */
char post_date [9]; /* Date message was posted */
char who_to [36]; /* Recipient to whom msg is sent */
char who_from [36]; /* Sender who posted message */
char subject [73]; /* Subject line of message */
} MSGHDR_RECORD;
typedef struct { /* MSGTXT.BBS structure definition */
unsigned char str_len; /* This string is stored in memory */
char str_txt [255]; /* in Pascal format to reduce */
} MSGTXT_RECORD; /* overhead, so take care! */
/* The strings in the *.MSG file header (Opus style) aren't Pascal */
/* type strings but have the 'normal' ASCIIZ format. */
typedef struct { /* OPUS-style (*.MSG) msg format */
char who_from [36]; /* Sender who posted message */
char who_to [36]; /* Recipient to whom msg is sent */
char subject [72]; /* Subject line of message */
char datetime [20]; /* Date/time msg was last edited */
unsigned int times_read; /* # of times message was read */
unsigned int dest_node; /* Destination node */
unsigned int orig_node; /* Origin node */
unsigned int cost; /* Cost to send netmail msg */
unsigned int orig_net; /* Origin net */
unsigned int dest_net; /* Destination net */
unsigned int dest_zone; /* Destination zone (These fields) */
unsigned int orig_zone; /* Origin zone (were padded ) */
unsigned int dest_point; /* Destination point (with 8 0's ) */
unsigned int orig_point; /* Origin point (in FSC-0001 ) */
unsigned int reply_to; /* Msg # to which this one replies */
unsigned int attribute; /* Msg attributes. Bits as follows: */
/* 0 : Private */
/* 1 : Crash */
/* 2 : Received */
/* 3 : Sent */
/* 4 : File attached */
/* 5 : In transit */
/* 6 : Orphan */
/* 7 : Kill when sent */
/* 8 : Local */
/* 9 : Hold for pickup */
/* 10 : UNUSED */
/* 11 : File request */
/* 12 : Return receipt request */
/* 13 : Is a return receipt */
/* 14 : Audit request */
/* 15 : File update request */
unsigned int next_reply; /* Next msg in reply chain */
} NET_RECORD;
16
============================================================
4. TEXT SERVICES
============================================================
=======================
txt_new ()
=======================
Name txt_new - create a new message text body
Usage M_TEXT txt_new (char * string);
Description The txt_new () function creates a new message text
body. This is typically the first function to call
when starting with a new message text. It requires
that you have defined a text handle previously.
This function also initializes the text handle.
The string argument may either be the first line
of the new message text body, or may be a pointer
to a complete null-terminated block of text.
Return value Text handle, NULL if error. In case of error, the
errortype and errorstring variables are set to
indicate the type of error.
Remarks The \n character is not translated and will be
placed in the message text body as a line feed
(LF). It will be ignored by message readers,
according to specifications in FTS-0001. The \r
character generates a Hard Carriage Return (HCR).
See also txt_add (), txt_dispose ()
Example See txt_add ()
=======================
txt_add ()
=======================
Name txt_add - add new text to an existing message text
body.
Usage int txt_add (M_TEXT texthandle, char * string);
Description The txt_add () function adds text to an existing
message text body. When starting a new message
text body, you MUST call txt_new () first in order
to initialize your text handle.
Return value Zero to indicate success, non-zero to indicate an
error. See the 'variables' section for completion
codes.
Remarks See txt_new ()
See also txt_new ()
17
Example #include "mblib.h"
M_TEXT msgtext;
msgtext = txt_new ("This is line one. \n");
if (msgtext == NULL) { /* ERROR! */
printf ("%s\n", errorstring);
exit (1);
}
if (txt_add (msgtext, "This is line two!\r"))
printf ("%s\n", errorstring);
========================
txt_dispose
========================
Name txt_dispose - dispose of a message text body
Usage void txt_dispose (M_TEXT texthandle);
Description The txt_dispose () function is used to dispose of
a block of text. It releases the memory allocated
for the text body. Call this function after you've
finished with a text body (after processing it),
to free memory and the text handle for future use.
Return value None.
See also txt_new ()
========================
txt_dump
========================
Name txt_dump - dump a message text to an output device
Usage int txt_dump (M_TEXT texthandle,
int (* output) (char *), unsigned char margin,
unsigned char kludges);
Description The txt_dump () function is used for all dumping
and printing of message text. It is very flexible
and can be used to print message text to the
screen, to send it to *any* device, file, or
whatever, or to manipulate individual text lines.
It does so by dividing text into separate lines,
and then calling a programmer supplied output
routine to process that line. Because you supply
the output routine yourself, you have complete
control over what happens to the text.
Text is wrapped to a specified line width (the
margin parameter), while kludges are either
printed or not printed, as specified by the
kludges parameter.
Because the syntax is a bit complicated, here's a
list summarizing the accepted parameters:
18
M_TEXT texthandle - this is the text handle for
the text body you want to dump.
int (* output) (char *) - this looks complex but
it isn't. This parameter is the NAME (!) of the
programmer supplied function to process a text
line. This function should accept a pointer to
char, and return an integer. This return value
should be zero to indicate success or nonzero to
indicate an error. See example below.
unsigned char margin - this is the number of
characters you want the text to be wrapped to
unsigned char kludges - this is a flag which
indicates whether to print kludges or not. It can
have the value KLUDGES to print kludges, or
NOKLUDGES to print no kludges.
Return value 0 if success, -1 if the output routine encountered
an error (or at least returned a non-zero value).
Remarks WARNING: Because the actual output is not handled
by MB_lib, an error encountered by the output
routine will not set the errortype and errorstring
variables! Error handling within the output
routine must be handled by the programmer.
The line passed to the output routine is not \n or
\r terminated.
Example #include "mblib.h"
#include <stdio.h>
int printline (char *); /* Always prototype */
int saveline (char *); /* these functions! */
M_TEXT msgtext;
FILE * savefile;
void main (void) {
msgtext = txt_new ("This is line one. \n");
if (msgtext == NULL) { /* ERROR! */
printf ("%s\n", errorstring);
exit (1);
}
if (txt_add (msgtext, "This is line two!\r")) {
printf ("%s\n", errorstring);
exit (1);
}
txt_dump (msgtext, printline, 70, NOKLUDGES);
/* Print text to screen, margin 70 */
if ((savefile = fopen ("TEST", "w")) == NULL)
exit (1);
txt_dump (msgtext, saveline, 80, KLUDGES);
/* Save text to file, margin 80 + kludges */
txt_dispose (msgtext);
puts ("Done.");
fclose (savefile);
} /* main */
/* The following functions each output text */
19
/* one line at a time */
int printline (char * line) {
puts (line);
return (0); /* This always works */
} /* printline */
int saveline (char * line) {
if (!fprintf (savefile, "%s\n", line)) {
puts ("Error saving text!");
return (-1); /* Error writing file */
}
else
return (0);
} /* saveline */
20
============================================================
5: MESSAGE BASE ACCESS SERVICES
============================================================
========================
msg_open
========================
Name msg_open - open the message base
Usage int msg_open (char * msgbasepath);
Description Before you can access the message base, you MUST
call this function to open the message base and
initialize the message base I/O system. If you do
not, any attempts to access the message base will
result in a MNO_ERR return value.
MB_lib tries to locate the message base files in
the path you supply. If the files don't exist,
msg_open () will create them.
Return value 0 if success, non-zero if error. See the 'varia-
bles' section for completion codes.
Remarks Attempts to open the message base when it is
already open can't do any harm. MB_lib keeps track
of these things, just in case you do not. :-)
The message base path may be terminated by a
backslash, or not.
See also msg_close ()
Example if (msg_open ("C:\\MSGBASE"))
exit (1); /* Error opening message base */
/* Your message handling goes here */
msg_close ();
========================
msg_close
========================
Name msg_close - close the message base
Usage void msg_close (void);
Description msg_close closes the message base files after
you're done with them. Closing the message base
when it isn't open can't do any harm - MB_lib
checks first.
Remarks You don't rally need to call msg_close () before
exiting your program. When you have opened the
message base, MB_lib checks to see if you've
closed them again before exiting. So if you forget
to call msg_close (), there's no harm done. But
from a puristic point of view, it's bad habit to
21
rely on such mechanisms, it's neater to close the
message base yourself.
See also msg_open ()
Example See msg_open ()
22
============================================================
6: MESSAGE BASE LOCKING SERVICES
============================================================
========================
msg_lock
========================
Name msg_lock - lock the message base to prevent writes
by other processes
Usage int msglock (char * semapath);
Description In a multi-tasking environment (like multi-line
BBS'es) we must take precautions to prevent file
corruption. The details on this are described
above (see Introduction to file locking).
The semapath string must specify the directory
that is used by Remote Access to keep it's
semaphore files in.
Return value -1 if the there was a problem locking the message
base, otherwise 0.
Remarks If SHARE is not loaded before locking the message
base, this won't cause an error. I guess that's
the whole point for the rather exotic (and, to my
taste, a little inelegant) RA locking method.
Since msg_lock () allows for a 15 second timeout,
locking your message base could take this long.
See also msg_unlock ()
Example #include "mb_lib.h"
if (msg_lock == -1)
puts ("Error locking message base");
/* Or you could put a few retries here... */
else {
msg_write_new (& msgheader, msgtext);
/* msgheader and msgtext previously inited */
msg_unlock ();
========================
msg_unlock
========================
Name msg_unlock - unlock the message base after
modifying it
Usage void msg_unlock (void);
Description Use msg_unlock () to release file locks on the
message base, thereby enabling other processes to
perform their write operations again.
Remarks See msg_lock ()
23
See also msg_lock ()
Example See msg_lock ()
24
============================================================
7: MESSAGE MANIPULATION SERVICES
============================================================
========================
msg_msgnr2recnr
========================
Name msg_msgnr2recnr - convert message number to record
number.
Usage long msg_msgnr2recnr (unsigned int msgnr);
Description Message numbers are not related to record numbers
on a 1:1 basis. Since messages may have been
deleted from the message base, the message base
records 0, 1, and 2 could, for example, contain
the messages 100, 294 and 1583. This function
converts a record number to the corresponding
message number.
Return value The record number of the message we're searching
for, -1 if the message was not found or an error
occurred. In the latter case the errortype and
errorstring variables are set.
Remarks If the message base contains message numbers which
have been set to -1 (some programs do this to
indicate that a message has been killed),
msg_msgnr2recnr () switches to a different search
algorithm, resulting in a slight decrease in
performance.
See also msg_recnr2msgnr ();
Example #include "mb_lib.h"
long recnr;
int msgnr;
printf ("Enter message number: ");
scanf ("%d", & msgnr);
if ((recnr = msg_msgnr2recnr (msgnr)) == -1)
puts ("Message not found.");
else
printf ("Record nr. %l\n", recnr);
========================
msg_recnr2msgnr
========================
Name msg_recnr2msgnr - convert record number to message
number.
Usage unsigned int msg_recnr2msgnr (long recnr);
25
Description This function does exactly the opposite of the
msg_msgnr2recnr () function: it retrieves the
message number that corresponds with a certain
record number.
Return value The message number of the message corresponding
with the specified record, -1 if an error
occurred. In this case the errortype an error-
string variables are set.
See also msg_msgnr2recnr ()
Example #include "mb_lib.h"
long recnr;
puts ("Message nr: 10");
if ((recnr = msg_recnr2msgnr (recnr)) == -1)
puts ("Error accessing message base");
else
printf ("Record nr: %l\n", recnr);
========================
msg_hdr_clear
========================
Name msg_hdr_clear - clear a message header record,
e.g. set all fields in the header to zero.
Usage void msg_hdr_clear (MSGHDR_RECORD * hdr);
Description A message header record contains many fields, many
of which should be set to 0 when creating a new
message. Because the fields in an uninitialized
header contain unknown values, we should initiali-
zed each field before using this header. Since
this is very cumbersome, this function does the
job for you.
Remarks This function should always be called when
creating a new message header, otherwise unpredic-
table effects will occur.
Example #include "mb_lib.h"
MSGHDR_RECORD hdr;
msg_hdr_clear (& hdr);
strcpy (hdr.who_from, "Frank Van.wensveen");
strcpy (hdr.who_to, "All");
strcpy (hdr.subject, "Test"); /* That's all! */
========================
msg_fixup4d
========================
26
Name msg_fixup4d - convert the zone fields in a netmail
header to an INTL kludge to overcome the FSC-0001
compliance problem.
Usage int msg_fixup4d (MSGHDR_RECORD * hdr, M_TEXT txt);
Description FSC-0001 describes the format of a basic *.MSG
netmail message header. Since this document dates
from the time that only 2D addressing (net/node)
was used, the definition had not defined any zone
and point address fields, but instead contains a 8
byte 'padding area'.
The header used in this message base library is a
variant that is widely used by a number of
popular software packages, but not supported by
all programs. This leads to problems. For example,
a message entered in Remote Access addressed to
zone 3, will be sent to the zone defined as the
mailers primary address instead.
To overcome this incompatibility, the INTL, FMPT
and TOPT kludges have been defined. These kludges
are added to the message text, usually at the top
of the message, although at the bottom they work
just as well. They have the following functions:
INTL 27:1331/703 2:285/504
indicates a message to 27:1331/703 from 2:285/504,
FMPT 2
indicates a message from point 2, and
TOPT 6
indicates a message to point 6.
So, a message from, say, 27:1331/703.5 to
2:285/504.2 will contain the following kludges:
INTL 2:285/504 27:1331/703
FMPT 5
TOPT 2
while the header has to contain only 1331/703 as
the origin address, and 285/504 as the destination
address. It may contain more, but if the mailer
(or any other software used to process the
message) doesn't support use of those fields, the
extra header information is ignored. Software that
doesn't support these kludges simply ignores them,
as with all other kludges.
The msg_fixup4d function allows you to easily
write messages that are processed correctly by
FSC-0001 compliant software. It converts the
undocumented zone fields in the Hudson netmail
header into a INTL kludge, while the related
net_fixup4d () function converts the undocumented
zone and point fields in the *.MSG header into
INTL, FMPT and TOPT kludges. If these fields are
zero, however, the kludges are omitted.
Return value Zero on success, nonzero on error.
Remarks CAUTION: The fixup functions add text to the
specified text handle. The text handle *MUST*
therefore be initialized, otherwise "unpredictable
27
results" (an euphemism for a total screwup,
usually) will occur. You *must* have called
txt_new () previous to calling msg_fixup4d () or
net_fixup4d ().
See also net_fixup4d ()
Example #include "mb_lib.h"
MSGHDR_RECORD hdr;
M_TEXT msgtxt;
msg_hdr_clear (& hdr);
strcpy (hdr.who_from, "Frank Van.wensveen");
strcpy (hdr.who_to, "Sysop");
strcpy (hdr.subject, "Kludges");
hdr.dest_zone = 27;
hdr.orig_zone = 2;
hdr.dest_net = 1331;
hdr.orig_net = 285;
hdr.dest_node = 703;
hdr.orig_node = 504;
/* Now we have two possibilities. The first: */
msgtxt = txt_new (""); /* THIS IS VITAL!! */
msg_fixup4d (& hdr, msgtxt); /* Add kludges */
txt_add ("This message now contains kludges.\r");
/* The second: */
msgtxt = txt_new ("This message is kludgy!\r");
msg_fixup4d (& hdr, msgtxt); /* Add kludges */
/* Note the \r after the last message text! */
/* Without it, the kludge will be ignored! */
msg_write_new (& hdr, msgtxt); /* Save it */
28
============================================================
8: MESSAGE READING SERVICES
============================================================
========================
msg_read_info
========================
Name msg_read_info - read MSGINFO.BBS into the global
msginfo record
Usage void msg_read_info (void);
Description MSGINFO.BBS does not contain multiple records, but
instead has a fixed length, and contains only one
structure that can have only one value at a time.
Therefore it makes sense not to read MSGINFO.BBS
over and over again to locally defined structures,
but instead use a global structure that can be
used for multiple purposes. This structure is
msginfo. See the variables section for details.
Return value Zero on success, a nonzero value on error,
indicating the nature of the error. See the
section on error handling for result codes.
See also msg_write_info ();
Example #include "mb_lib.h"
/* Message base is supposed to be open */
if (msg_read_info ())
exit (1); /* Read error */
printf ("Number of messages in base: ");
printf ("%u\n", msginfo.total_msgs);
========================
msg_read_hdr
========================
Name msg_read_hdr - read a message header from a
specified record in MSGHDR.BBS.
Usage int msg_read_hdr (long recnr,
MSGHDR_RECORD * hdr);
Description This function is used to read a message header.
Prior to reading the header, the record number of
the message must be known. This number can be
obtained from one of the search functions.
Return value Zero on success, a nonzero value on error,
indicating the nature of the error. See the
section on error handling for result codes.
See also msg_write_hdr ()
29
Example #include "mblib.h"
MSGHDR_RECORD myheader;
/* Reading the header in record 100: */
if (msg_read_hdr (100, & myheader))
exit (1); /* Read error */
printf ("Subject: %s\n", myhdr.subject);
========================
msg_read_idx
========================
Name msg_read_idx - read a message header from a
specified record in MSGIDX.BBS.
Usage int msg_read_idx (long recnr,
MSGIDX_RECORD * idx);
Description This function is used to read a message index
record. This record specifies the message number
and the board number of a message in a certain
record. Prior to reading the record, the record
number of the message must be known. This number
can be obtained from one of the search functions.
Return value Zero on success, a nonzero value on error,
indicating the nature of the error. See the
section on error handling for result codes.
See also msg_write_idx ()
Example #include "mblib.h"
MSGIDX_RECORD myidx;
/* Reading the index in record 100: */
if (msg_read_idx (100, & myidx))
exit (1); /* Read error */
printf ("Msg nr: %d, board nr: %d",
myidx.msg_num, myidx.board);
========================
msg_read_toidx
========================
Name msg_read_toidx - read a record from a specified
record in MSGTOIDX.BBS.
Usage int msg_read_toidx (long recnr,
MSGTOIDX_RECORD * idx);
Description This function is used to read a MSGTOIDX.BBS
record. Prior to reading the header, the record
number of the message must be known. This number
can be obtained from one of the search functions.
30
The obtained record specifies the user name the
message is sent to. After the message has been
received, this record should be overwritten by the
message reader. As a result, the MSGTOIDX file can
be searched for unreceived mail to a certain user.
Return value Zero on success, a nonzero value on error,
indicating the nature of the error. See the
section on error handling for result codes.
See also msg_write_toidx ()
Example #include "mblib.h"
MSGTOIDX_RECORD addressee;
/* Reading addressee from record 100: */
if (msg_read_toidx (100, & addressee))
exit (1); /* Read error */
printf ("To: %s\n", addressee);
========================
msg_read_text
========================
Name msg_read_text - read the text of a message, the
header of which is known.
Usage M_TEXT msg_read_text (MSGHDR_RECORD * hdr);
Description This function is used to read the text of a
certain message. Because text is not just a single
record, more information than just a record number
is needed. Therefore, the message header must be
read prior to calling this function, and a pointer
to the corresponding message header is passed to
this read function.
Return value The text handle to be used for the text, NULL if
an error occurred. In this case the errortype and
errorstring variables are set.
See also msg_write_text (), txt_dump ()
Example #include "mb_lib.h"
MSGHDR_RECORD msghdr; /* Header */
M_TEXT msgtxt; /* Text handle */
/* Reading message in record 100 */
if (msg_read_hdr (100, & msghdr))
exit (1); /* Read error */
msgtxt = msg_read_text (& msghdr);
if (msgtxt == NULL)
exit (1); /* Read error */
printf ("Message %d:\n", msghdr.msgnum);
printf ("By: %s\nTo:%s\nRe:%s\n\n",
msghdr.who_from, msghdr.who_to,
msghdr.subject);
31
txt_dump (msgtxt, printline, 70, NOKLUDGES);
/* Printline function defined elsewhere */
32
============================================================
9: MESSAGE WRITING SERVICES
============================================================
========================
msg_write_info
========================
Name msg_write_info - write the global msginfo record
to MSGINFO.BBS
Usage void msg_write_info (void);
Description This function is much like msg_read_info, except
that it writes the global MSGINFO.BBS record back,
for example after it has been modified.
Return value Zero on success, a nonzero value on error,
indicating the nature of the error. See the
section on error handling for result codes.
See also msg_read_info ();
Example #include "mb_lib.h"
/* Message base is supposed to be open */
if (msg_read_info ())
exit (1); /* Read error */
/* The msginfo record is modified here */
if (msg_write_info ())
exit (1); /* Write error */
========================
msg_write_hdr
========================
Name msg_read_hdr - write a message header to a
specified record in MSGHDR.BBS.
Usage int msg_write_hdr (long recnr,
MSGHDR_RECORD * hdr);
Description This function is used to write a message header.
Prior to writing the header, the record number of
the message must be known. This number can be
obtained from one of the search functions, or,
when modifying a header, it is usually known
before you need this function.
Return value Zero on success, a nonzero value on error,
indicating the nature of the error. See the
section on error handling for result codes.
See also msg_read_hdr ()
Example #include "mblib.h"
MSGHDR_RECORD myheader;
33
/* Modify the header in record 100: */
if (msg_read_hdr (100, & myheader))
exit (1); /* Read error */
myheader.times_read ++; /* Read once more */
if (msg_write_hdr (100, & myheader))
exit (1); /* Write error */
========================
msg_write_idx
========================
Name msg_write_idx - write a message header to a
specified record in MSGIDX.BBS.
Usage int msg_write_idx (long recnr,
MSGIDX_RECORD * idx);
Description This function is much like msg_read_idx (), except
that it writes a record to MSGIDX.BBS.
Return value Zero on success, a nonzero value on error,
indicating the nature of the error. See the
section on error handling for result codes.
See also msg_read_idx ()
Example #include "mblib.h"
MSGIDX_RECORD myidx; /* Supposed to be inited */
/* Writing the index to record 100: */
if (msg_to_idx (100, & myidx))
exit (1); /* Read error */
========================
msg_write_toidx
========================
Name msg_write_toidx - write a record to a specified
record in MSGTOIDX.BBS.
Usage int msg_write_toidx (long recnr,
MSGTOIDX_RECORD * idx);
Description This function is much like msg_read_toidx (),
except that it writes a record to MSGIDX.BBS, for
example after modifying it.
Return value Zero on success, a nonzero value on error,
indicating the nature of the error. See the
section on error handling for result codes.
See also msg_read_toidx ()
Example #include "mblib.h"
MSGTOIDX_RECORD addressee;
34
/* Reading addressee from record 100: */
if (msg_read_toidx (100, & addressee))
exit (1); /* Read error */
printf ("To: %s\n", addressee);
strcpy (addressee, "* Received *");
/* Since the msg is no longer unreceived, */
/* mark it as such */
if (msg_write_toidx (100, & addressee)
exit (1); /* Write error */
========================
msg_write_text
========================
Name msg_write_text - write text to a message, either
modifying old text or creating new text.
Usage int msg_write_text (MSGHDR_RECORD * hdr,
M_TEXT txt);
Description This function is typically used to modify existing
message text, or to add new text to a previously
written new message header. To create entire new
messages from scratch, you'd better use the
msg_write_new () function.
Whether the text is used as a replacement or as
new text is determined by the message header
passed to the function. If the start_block and
num_blocks field in the message header are zero,
the text is treated as new text (since there
obviously isn't any old text with these field
containing zero). Otherwise, previous text is
replaced.
Return value Zero on success, a nonzero value on error,
indicating the nature of the error. See the
section on error handling for result codes.
Remarks Replaced message text blocks are not actually
removed from the message base. You need to pack
the message base with an external message base
maintenance utility to do this.
See also msg_read_text (), msg_write_new ()
Example #include "mb_lib.h"
MSGHDR_RECORD msghdr; /* Header */
M_TEXT msgtxt; /* Text handle */
/* Reading message in record 100 */
if (msg_read_hdr (100, & msghdr))
exit (1); /* Read error */
/* Now read text, add line, write it back */
msgtxt = msg_read_text (& msghdr);
if (msgtxt == NULL)
exit (1); /* Read error */
35
if (txt_add (msgtxt, "One more line..."))
exit (1); /* Memory error */
if (msg_write_txt (& msghdr, msgtxt))
exit (1); /* Error writing new text */
========================
msg_write_new
========================
Name msg_write_new - write a new message to the message
base
Usage int msg_write_new (MSGHDR_RECORD * hdr,
M_TEXT txt);
Description Ah - this is the one! This function is typically
used to write a new message to the message base.
Before calling this function you need to construct
a message header and fill in the source, destina-
tion and subject fields (in case of netmail, node
numbers as well), fill in the board number and
set the desired message attributes. You also need
to create a message text. When you've got header
and text handy, all you need is call this function
(which will set the other header fields for you).
Return value Zero on success, a nonzero value on error,
indicating the nature of the error. See the
section on error handling for result codes.
Remarks You must set unused header fields to zero - the
msg_hdr_clear () function can be used for this. If
you don't, unpredicted results (this being an
euphemism for a complete screwup) could occur.
See also msg_write_text (), msg_kill ()
Example #include "mb_lib.h"
MSGHDR_RECORD msghdr;
M_TEXT msgtxt;
/* Error checking left out to keep it short! */
/* In other words, this is quick and dirty. */
main () {
msg_open ("C:\\MSGBASE");
msg_hdr_clear (& msghdr);
strcpy (msghdr.who_from, "Frank Van.wensveen");
strcpy (msghdr.who_to, "All");
strcpy (msghdr.subject, "See how easy it is?");
msghdr.board = 6; /* My local echomail board */
msghdr.msg_attr = MA_LOCAL;
msgtxt = txt_new ("See? Writing a message is ");
txt_add (msgtxt, "easy! Even I could do it!\r");
msg_lock ("C:\\SEMAFORE");
msg_write_new (& msghdr, msgtxt);
msg_unlock ();
msg_close ();
36
}
========================
msg_kill
========================
Name msg_kill - kill a message
Usage int msg_kill (long recnr);
Description The msg_kill () function is used to kill a
message, the record number of which is specified.
Return value Zero on success, a nonzero value on error,
indicating the nature of the error. See the
section on error handling for result codes.
Remarks A killed message is not actually removed from the
message base. To do this, you need to pack the
message base with an external message base
maintenance utility.
Example #include "mb_lib.h"
long recnr;
/* Killing message nr. 100 */
recnr = msg_msgnr2recnr (100);
if (recnr != -1)
msg_kill (recnr);
/* Error checking left out */
37
============================================================
10: MESSAGE SEARCH SERVICES
============================================================
========================
msg_firstinboard
========================
Name msg_firstinboard - find the first message in the
specified board
Usage long msg_firstinboard (unsigned char board);
Description This message is used to find the RECORD NUMBER of
the first message in a certain board. You'll need
things like this when reading all messages in a
certain board from first to last or something
like that.
Return value The RECORD number of the first message in the
specified board, -1 if not found or error. In the
latter case the errortype and errorstring
variables are set.
See also msg_lastinboard (), msg_nextinboard (),
msg_previnboard ()
Example See msg_nextinboard ();
========================
msg_nextinboard
========================
Name msg_nextinboard - find the next message in the
same board as the message we've just read is in.
Usage long msg_nextinboard (long recnr);
Description After calling msg_firstinboard, we've got the
record number of the first message in that board.
This function finds the next message in that
board, using the record number of the previously
processed message as a starting point.
Return value The RECORD number of the next message in the
specified board, -1 if not found or error. In the
latter case the errortype and errorstring
variables are set.
See also msg_firstinboard (), msg_lastinboard (),
msg_previnboard ();
Example #include "mb_lib.h"
long recnr;
/* Listing messages in board 1 */
recnr = msg_firstinboard (1);
38
while (recnr != -1) {
printf ("Message number:%d\n",
msg_recnr2msgnr (recnr);
recnr = msg_nextinboard (recnr);
}
========================
msg_lastinboard
========================
Name msg_lastinboard - find the last message in the
specified board.
Usage long msg_lastinboard (unsigned char board);
Description This message is identical to msg_firstinboard, but
returns the number of the last message instead of
the first.
Return value The RECORD number of the last message in the
specified board, -1 if not found or error. In the
latter case the errortype and errorstring
variables are set.
See also msg_firstinboard (), msg_nextinboard (),
msg_previnboard ();
Example See msg_previnboard ();
========================
msg_previnboard
========================
Name msg_previnboard - find the previous message in the
same board as the message we've just read is in.
Usage long msg_previnboard (long recnr);
Description This function is identical to msg_nextinboard (),
except that it searches backwards.
Return value The RECORD number of the previous message in the
specified board, -1 if not found or error. In the
latter case the errortype and errorstring
variables are set.
See also msg_next_inboard, msg_lastinboard (),
msg_previnboard ();
Example #include "mb_lib.h"
long recnr;
/* Backward listing messages in board 1 */
recnr = msg_lastinboard (1);
while (recnr != -1) {
printf ("Message number:%d\n",
39
msg_recnr2msgnr (recnr);
recnr = msg_previnboard (recnr);
}
========================
msg_firstto
========================
Name msg_firstto - find the first unreceived message to
a certain user
Usage long msg_firstto (MSGTOIDX_RECORD * idx);
Description This message can be used to scan the message base
for unreceived mail to a certain user. It scans
MSGTOIDX.BBS for the user's name and returns the
record number associated with a search hit. You
can then proceed to read the message in this
record.
Software used for reading the unreceived messages
should overwrite the corresponding record in
MSGTOIDX.BBS to prevent the message from remaining
marked as unreceived. Functions in MB_lib don't
take care of this, the programmer must do it.
Return value The RECORD number of the first message to the
specified user, -1 if not found or error. In the
latter case the errortype and errorstring
variables are set.
See also msg_nextto (), msg_lastto (), msg_prevto ()
Example See msg_nextto ()
========================
msg_nextto
========================
Name msg_nextto - find the next message to the same
user as the one we just read was addressed to
Usage long msg_nextto (long recnr);
Description After calling msg_firstto, we've got the record
number of the first message to a certain user.
This function finds the next message to that
user, using the record number of the previously
processed message as a starting point.
Return value The RECORD number of the next message to the
specified user, -1 if not found or error. In the
latter case the errortype and errorstring
variables are set.
See also msg_firstto (), msg_lastto (), msg_prevto ()
40
Example #include "mb_lib.h"
long recnr;
MSGTOIDX_RECORD toidx;
strcpy (toidx, "Frank Van.wensveen");
recnr = msg_firstto (toidx);
puts ("Mail waiting for you:");
while (recnr != -1) {
printf ("Message number: %d\n",
msg_recnr2msgnr (recnr);
recnr = msg_nextto (recnr);
}
========================
msg_lastto
========================
Name msg_lastto - find the last unreceived message to a
certain user.
Usage long msg_lastto (MSGTOIDX_RECORD * toidx);
Description This function is identical to msg_firstto (),
except that it searches for the last message
instead of the first.
Return value The RECORD number of the last message to the
specified user, -1 if not found or error. In the
latter case the errortype and errorstring
variables are set.
See also msg_firstto (), msg_nextto (), msg_prevto ()
Example See msg_prevto ()
========================
msg_prevto
========================
Name msg_prevto - find the previous message to the same
user as the one we just read was addressed to
Usage long msg_nextto (long recnr);
Description This message is identical to msg_nextto (), except
that it searches backwards.
Return value The RECORD number of the previous message to the
specified user, -1 if not found or error. In the
latter case the errortype and errorstring
variables are set.
See also msg_firstto (), msg_lastto (), msg_nextto ()
Example #include "mb_lib.h"
long recnr;
41
MSGTOIDX_RECORD toidx;
strcpy (toidx, "Frank Van.wensveen");
recnr = msg_lastto (toidx);
puts ("Mail waiting for you:");
puts ("Listed last to first")
while (recnr != -1) {
printf ("Message number: %d\n",
msg_recnr2msgnr (recnr);
recnr = msg_prevto (recnr);
}
42
============================================================
11: NETMAIL (*.MSG) SERVICES
============================================================
The following routines make up the net_ family. These functions
are typically used for netmail support. But in fact any *.MSG
base is supported, no matter what type of mail it contains.
(Fmail for example uses *.MSG type files to store personal mail.)
WARNING: though the netmail header defined in MB_LIB.H contains
both a Zone and a Point field, these fields are not supported by
FSC-0001. Correct interpretation of these fields by all software
is NOT guaranteed. In fact, some programs have been verified to
use these fields for other data. FSC-0001 (sec) specified 2D
addressing only.
In order to use 4D addresses which will be processed correctly by
all software, it is recommended that you use the FMPT/TOPT and
INTL kludges instead.
43
========================
net_first
========================
Name net_first - get the number of the first netmail
message in a directory.
Usage int net_first (char * msgpath);
Description This function is used to get the number of the
first message in a *.MSG (Opus-style) message
base. The path to the directory holding the *.MSG
files is specified.
Return value The number of the first message in the directory,
0 if error or no messages found. In this case the
errortype and errorstring variables are set.
Remarks The specified directory is supposed to contain
*.MSG files. These message need not be netmail
messages.
See also net_last (), net_next (), net_prev ()
Example See net_next ()
========================
net_next
========================
Name net_next - return the number of the message next
to the specified one.
Usage int net_next (char * msgpath, int nr);
Description While net_first () return the number of the first
message in the specified directory, this function
returns the number of the message next to the
specified one. Typically you'd call net_first (),
and use the number obtained to find the next
message until you run out of mail.
Return value The number of the message in the directory next to
the specified number, 0 if error or no messages
found. In this case the errortype and errorstring
variables are set.
See also net_first (), net_last (), net_prev ()
Example #include "mb_lib.h"
int msgnum;
msgnum = net_first ("C:\\NETMAIL");
puts ("Netmail:");
while (msgnum != 0) {
printf ("%d\n", msgnum);
msgnum = net_next (msgnum);
44
}
========================
net_last
========================
Name net_last - get the number of the last netmail
message in a directory.
Usage int net_last (char * msgpath);
Description This function is used to get the number of the
last message in a *.MSG (Opus-style) message
base. The path to the directory holding the *.MSG
files is specified.
Return value The number of the last message in the directory,
0 if error or no messages found. In this case the
errortype and errorstring variables are set.
Remarks The specified directory is supposed to contain
*.MSG files. These message need not be netmail
messages.
See also net_first (), net_next (), net_prev ()
Example See net_prev ()
========================
net_prev
========================
Name net_prev - return the number of the message
previous to the specified one.
Usage int net_prev (char * msgpath, int nr);
Description While net_last () returns the number of the last
message in the specified directory, this function
returns the number of the message previous to the
specified one. Typically you'd call net_last (),
and use the number obtained to find the previous
message until you run out of mail.
Return value The number of the message in the directory
previous to the specified number, 0 if error or
no messages found. In this case the errortype and
errorstring variables are set.
See also net_first (), net_last (), net_next ()
Example #include "mb_lib.h"
int msgnum;
msgnum = net_last ("C:\\NETMAIL");
puts ("Netmail listed backwards:");
45
while (msgnum != 0) {
printf ("%d\n", msgnum);
msgnum = net_prev (msgnum);
}
========================
net_hdr_clear
========================
Name net_hdr_clear - clear a netmail header record,
e.g. set all fields in the header to zero.
Usage void net_hdr_clear (NET_RECORD * hdr);
Description A netmail header record contains many fields, many
of which should be set to 0 when creating a new
message. Because the fields in an uninitialized
header contain unknown values, we should initiali-
zed each field before using this header. Since
this is very cumbersome, this function does the
job for you.
Remarks This function should always be called when
creating a new netmail header, otherwise unpredic-
table effects will occur.
Example #include "mb_lib.h"
NET_RECORD hdr;
net_hdr_clear (& hdr);
strcpy (hdr.who_from, "Frank Van.wensveen");
strcpy (hdr.who_to, "All");
strcpy (hdr.subject, "Test");
/* The header is now fully initialized! */
========================
net_getlastread
========================
Name net_getlastread - get the lastread pointer for the
specified *.msg folder.
Usage int net_getlastread (char * msgpath);
Description To keep track of what message has been read the
last time, most message readers / editors use a
lastread pointer. This is a small file located in
the message directory, which contains the number
of the message that was read last.
This function is used to read the lastread pointer
for a specified message directory.
Return value The number of the message that was read last or -1
if an error was encountered. In that case the
errortype and errorstring variables are set.
46
See also net_setlastread ()
========================
net_setlastread
========================
Name net_setlastread - set the lastread pointer for the
specified *.msg folder
Usage int setlastread (char * msgpath, int lastread);
Description This function is used to set the lastread pointer
for the specified message directory to a specified
value.
Return value Zero if success, non-zero if an error was
encountered. In this case the errortype and
errorstring variables are set.
See also net_getlastread
========================
net_read_hdr
========================
Name net_read_hdr - read a netmail header
Usage int net_read_hdr (char * msgpath, int msgnr,
NET_RECORD * hdr);
Description This function is much like msg_read_hdr, in that
it is used to read a message header.
Return value Zero if success, non-zero if an error was
encountered.
See also msg_read_hdr (), net_read_text ()
Example See net_read_text ()
========================
net_read_text
========================
Name net_read_text - read the text body of a netmail
message.
Usage M_TEXT net_read_text (char * msgpath, int msgnum);
Description This function is much like msg_read_text, in that
it is used to read the text body of a message.
Combined with the net_read_hdr () function, it can
be used for all kinds of netmail message reading
jobs. Because text is returned by means of a text
47
handle, all possibilities with regard to text
manipulation apply.
Return value Text handle, NULL on error. In this case the
errortype and errorstring variables are set.
See also msg_read_text (), net_read_hdr ()
Example #include "mb_lib.h"
M_TEXT txt;
NET_RECORD hdr;
/* Reading message nr. 10 */
if (net_read_hdr ("C:\\NETMAIL", 10, & hdr))
exit (1);
txt = net_read_text ("C:\\NETMAIL, 10);
if (txt == NULL)
exit (1);
txt_dump (txt, printline, 70, NOKLUDGES);
========================
net_write
========================
Name net_write - write a (netmail) message into a *.MSG
base.
Usage int net_write (char * msgpath, int msgnr,
NET_RECORD * hdr, M_TEXT txt);
Description This function is all you need to write a *.MSG
message. Before calling the net_write () function,
you need to determine the desired message number.
For new messages you typically do something like
msg_nr = net_last ("C:\\NETMAIL") + 1;.
You also need to create a message text, the text
handle of which is passed to the net_write ()
function.
Unused fields in the message header must be set to
zero, or unpredictable results will occur. The
net_hdr_clear () function can be used for that.
The datetime field in the message header does not
need to be initialized, net_write () will do that
for you.
Return value Zero on success, non-zero when an error is
encountered. In this case the errortype and
errorstring variables are set.
See also msg_write_new ();
Example #include "mb_lib.h"
NET_RECORD msghdr;
M_TEXT msgtxt;
/* Error checking left out to keep it short! */
48
/* In other words, this is quick and dirty. */
main () {
net_hdr_clear (& msghdr);
strcpy (msghdr.who_from, "Frank Van.wensveen");
strcpy (msghdr.who_to, "SysOp");
strcpy (msghdr.subject, "See how easy it is?");
msghdr.msg_attr = NM_LOCAL;
msgtxt = txt_new ("See? Writing a netmail is ");
txt_add (msgtxt, "easy! Even I could do it!\r");
net_write ("C:\\NETMAIL",
net_last ("C:\\NETMAIL") + 1,
& msghdr, msgtxt);
}
========================
net_kill
========================
Name net_kill - kill a *.MSG message.
Usage int net_kill (char * msgpath, int msgnr);
Description This function is used to kill (delete) a message
in a *.MSG base. Unlike messages in the Hudson
message base, *.MSG messages are physically
deleted when killed.
Return value Zero on success, non-zero if an error was
encountered. In this case the errortype and
errorstring variables are set.
========================
net_fixup4d
========================
Name net_fixup4d - convert the zone and point fields in
a *.MSG netmail header into INTL and FMPT/TOPT
kludges to overcome the FSC-0001 compliance
problem.
Usage int net_fixup4d (NET_RECORD * hdr, M_TEXT txt);
Description See msg_fixup4d. The difference between Hudson
netmail headers and *.MSG headers is that the
latter also contain unsupported point fields. The
Hudson netmail header does not support point
addresses.
Return value Zero on success, non-zero on error.
Remarks CAUTION: The fixup functions add text to the
specified text handle. The text handle *MUST*
therefore be initialized, otherwise "unpredictable
results" (an euphemism for a total screwup,
usually) will occur. You *must* have called
49
txt_new () previous to calling msg_fixup4d () or
net_fixup4d ().
See also msg_fixup4d ()
Example see msg_fixup4d ().
50
This page intentionally left blank.
51