Dec 082017
C News #19, Newsletter for C programmers.
File CNEWS019.ZIP from The Programmer’s Corner in
Category C Source Code
C News #19, Newsletter for C programmers.
File Name File Size Zip Size Zip Type
BLOB.C 4267 1619 deflated
CNEWS019.DOC 83731 24437 deflated
FONENUM.C 3694 1334 deflated

Download File CNEWS019.ZIP Here

Contents of the CNEWS019.DOC file

Issue C News 1

| C NEWS - International C Electronic Newsletter/Journal |
| "Dedicated to the Art of C Programming" |
| |
| Founded 12/27/87 |

Table of Contents

The HEAP: By Barry Lynch ............................... 2

The STACK: By Jim Singleton ............................ 3

String Arrays by Paul E. Castle II ....................... 4

Review: The ECSTR Library by Paul Hinds .................. 8

LABEL.C -- A Simple Label Program by Jim Singleton......... 10

Beginner's Column by Wayne Dernoncourt ................. 19

Where's That Number? by Jim Singleton ................... 24

Portability Coding Issues ............................ 26

New PD/Shareware Releases by Barry Lynch ................ 37

Article Submission Standards ......................... 39

How to get hold of us here at C............................ 40

"C News" is an Electronic Journal published from time to
time. The subject of C News is the C programming language,
as well as any derivatives like C++. All readers are
encouraged to submit articles, reviews, or comments for
submission. C News is freely distributed, but can not be
sold for a profit and cannot have a charge assessed to
cover distribution costs. All articles and reviews become
the property of C News and cannot be included in other
publications without written permission from the C News
Editorial Staff. This publication is Copyrighted under
U.S. Copyright Law.

Issue C News 2

The HEAP: By Barry Lynch

It's been quite awhile since an issue of C News has been
published, and it is with some pride that Dan, Wayne, Jim and I
bring this 19th issue to you.

Over the last few months I have had the pleasure of selling
a home in Virginia and relocating and purchasing a new home in
the Dallas-Fort Worth area. For those of you that own homes or
have been through the relocation process, you probably
understand the stress and upheaveal that a relocation brings.
Finally, after months of planning, packing, travelling and
unpacking I have settled down and look forward to more frequent
issues of C News.

This issue of C News brings some articles that were
submitted quite a few months ago and have been patiently waiting
for the next publication date. There are articles on C
portability problems, the continuation of Jim Singleton's
article on software development, another one of Wayne's
Beginner's columns and a piece on shareware.

The last few months have seen the release of a major
upgrade to one of the bigger C compilers (Microsoft), a major
release from one of the original DOS C compiler vendors
(Lattice) and new compiler to add more fuel to the compiler wars
(Topspeed). Over the next couple of months, Dan, Jim Wayne and
I will be bringing you some of our thoughts on these various
compilers as we all use different ones in our daily programming

This issue of C News is not the last and will be followed
by Issue 20 sometime in the early fall. If you have ideas for
articles or comments on the style that are constructive, we
welcome them. It should be noted that we are not striving to be
a "professional" publication, just a fun project by a couple of
guys in Virginia, and a misplaced "Brit" in Texas.

C ya in the next issue.


Issue C News 3

The STACK: By Jim Singleton

When Barry and I were discussing the editorial for this
issue of the C News, Barry suggested that I write one as well
since Dan, Wayne and I are still in the Washington, D.C. area.

Barry forgot to mention that the last few months have also
seen Borland come out with their first C++ compiler. While none
of us at C News are experienced C++ programmers, we would like
to cover C++ in the C News. Anyone interested in submitting an
article, please contact Barry or myself.

At the end of each issue we tell you how to contact us here
at the C News. However, in light of all the changes since issue
18, we decided to mention it here as well. You can contact us

The C News
P.O. Box 15314
Arlington, VA 22215

FidoNet: 109/138.1 (Jim)
Fidonet: 1:124/3116 (Barry)
North Texas C Programmers Board: 1-214-442-0223 (Barry)

CIS: 71540,2677 (Jim)

MCI Mail: 272-8067 (Barry)

Internet: [email protected] (Dan)
UUCP: ...!uunet!mimsy!dbk

European C News Distribution Point: 011-3185642891
Sysop: Rob Koel

Feel free to contact us with any ideas, comments, or
criticisms you may have.


Issue C News 4

String Arrays by Paul E. Castle II

Unfortunately, error conditions are a fact of life. Any
program you write, no matter how small, carries with it the
potential to produce an error. A good program attempts to trap
errors when they occur and gracefully handle them.

Error handling, however, can produce masses of extra code,
extreme confusion, and sticky maintenance problems if not
implemented smartly.

Consider the following line of code:

fileptr = fopen(infile,open_mode)

This is a very common and simple action - open a file.
However, as written, it is fraught with danger. Suppose the
open fails? The FILE pointer "fileptr" is assigned the "NULL"
value. Any attempt to use "fileptr" will, at best, cause a
program crash, or worse, a system crash. The solution is to
trap the error:

if((fileptr = fopen(infile,open_mode)) == NULL) {
printf("Could not open INPUT file.\n");
exit(1); }

This code traps the error, prints an error message to the
screen, and terminates processing.

Now, allow me to digress from the main topic for just a
moment and talk a little about the line "exit(1)".

The "exit()" function is part of the standard library. The
prototype can be found in "stdlib.h" and "process.h" and takes
the form of:

void exit(int status)

"exit()" flushes all buffers for files open for buffered I/O,
closes all files, and terminates the program returning the "int
status" to the calling program or the system. A "status" value
of "0" traditionally means "no error". Error conditions are
indicated by a non-zero value. Keep this in mind as we

Now, let's assume you have seven files that your program
opens. That means seven "fopen" statements and twenty-one lines

Issue C News 5

of code like that above. Furthermore, let's assume that you
have seven other error conditions for which you check. Each
check includes an "if" statement, a "printf" statement, and an
"exit" statement.

You should be able to see by now that you have a
substantial amount of code invested in opening files and error
handling. But, you can reduce the amount of code, and the
headaches associated with maintaining it, substantially by using
string arrays. Watch.

To handle the errors, create an array like this:

char *error_msg[] = {
"Error message one text.\n",
"Error message two text.\n",
"Error message three text.\n",
"Error message four text.\n",
"Error message five text.\n",
"Error message six text.\n",
"Error message seven text.\n",
"Error message eight text.\n" };

The above array is an array of pointers to strings. The
first entry, "error_msg[0]", is NULL (or NO ERROR).
"error_msg[1]" is the string "Error message one text." and so on
to "error_msg[8]".

Once the array has been created, you can now create a
routine called "error_exit". You would declare the function
like this:

void error_exit(int errnum)

The code for the routine would look like this:

void error_exit(int errnum)
if(errnum) {

To use "error_exit", the invoking function calls the error
routine passing the index value associated with the desired
error message. The "if(errnum)" determines if an error exists.
A "0" (or NO ERROR) would resolve to false and the function will
return to the caller taking no action. Obviously, you would
never call "error_exit" with a value of zero. A non-zero value

Issue C News 6

would resolve to true and the "printf" statement would print the
text pointed to by "error_msg[errnum] and the "exit" statement
will return the value of "errnum" to the calling program or

Using the above example of the "fopen", we would rewrite
the code in this way:

if((fileptr = fopen(infile,open_mode)) == NULL)

This would result in the error message "Error message one
text." being printed and the value of 1 being passed to the
calling program or system if an error occurs.

This method keeps the duplication of code to a minimum and
allows all error handling to be done from a single function. As
the code changes or more error conditions are checked for, it is
a simple matter to add error messages to the array or to change
existing messages. This saves you from having to search
thousands of lines of code for elusive error text to change.

Now, we have solved the problem of error handling, but we
still have seven different "fopen" statements in our program.
We can use a string array to solve this problem, also.

If we create the following arrays:

FILE *fileptr[7]; /* array of file pointers */

char *file_table[] = { /* array of file names */
"FILE7"; }

we can rewrite the "fopen" code to handle all seven files.

if((fileptr[file_ndx] = fopen(file_table[file_ndx],open_mode)) == NULL)

The invoking function would call the open routine passing
the index pointing to the desired file name in the
"file_table". That same index is used to access the array of
file pointers so that pointer "fileptr[0]" would point to
"file_table[0]", or FILE1 in this case, and so on.

Issue C News 7

If you wished to open all the files at one time, you could
do so in this manner:

for(file_ndx = 0;file_ndx < 7;file_ndx++) {
if((fileptr[file_ndx] = fopen(file_table[file_ndx],open_mode)) == NULL)
error_exit(1); }

As you can see, string arrays can be used to add
flexibility to your code and help make "generic" functions.
They are a powerful tool and with a little thought and a little
imagination you can do amazing things with them.

Issue C News 8

Review: The ECSTR Library by Paul Hinds


The .ZIP file is about 65K and is dated 5/86 (as are all of
the files) even though all of the files have internal comments
indicating dates of April/May/June, 1984. The file is stated by
the author as being placed in the public domain although he does
request that it not be put to any "military" use. I've seen this
file on MANY boards in the DC area, so assume it is fairly
readily available wherever you are.

General Description

With over 60 functions (74 files total, including .H files
and some others), all of which are concerned with string
manipulation (it is my assumption that ECSTR => Extended C
STRing), this is a wealth of source material for anyone who
wants their own source library of string manipulation routines.
The following files are included:



This collection is not exactly for the faint of heart or
the rank beginner (although anyone who has created, compiled,
linked and run even a few C functions can deal with it). Only

Issue C News 9

source code is provided, with excellent block comments in each
source module and a modest, but illuminating, README file.
Mildly oriented towards VAX machines but conditional
compile/assembly statements make everything available to non-VAX

I've stripped out the heart of several of the routines
(dumping the conditional stuff since I don't have a PDP-11 or a
VAX) and have have had no problem reformatting them to compile
under Turbo C (2.0). I see no reason why users of other
compilers should have any more trouble than I did.

Most or all of the 30 or so library functions for string
manipulation included in Turbo-C 2.0 can be found here, as well
as ANOTHER 30 or so, and since the source code is provided, this
is an excellent opportunity for those of you who want to have
LOTS of string functions available and/or those of you who want
source code so you can "do your own thing". I doubt that many
compilers (if any) provide anywhere near this extensive a
package of string functions. And, of course, the price is

This is a useful package, regardless of its age. There is,
of course, the fact that since it is an unsupported package,
you've got no one to yell at if anything goes wrong, but hey,
the source code is there and what more can you ask for?

COMPATIBILITY: written to run under ANY version of UNIX.
Author has wisely chosen compatibility over rationality (in
function names and argument lists) --- if this sounds strange,
then you might be unfamiliar with the MAJOR headaches which
accompany rational but incompatible tools. The READ.ME file goes
into some discussion about compatability with UNIX functions.

ANSI: this was done LONG before the ANSI standard was
introduced and I have made no attempt to determine ANSI

Issue C News 10

LABEL.C -- A Simple Label Program by Jim Singleton


The other day at work, I came across a simple diskette
label program, written in BASIC. The program printed a simple
four line label composed of a title, name, date and two comment

| Personnel Payroll Data, Disk |
| Rufus Jackson 6/23/90 |
| This is the data for use by |
| the payroll audit team. |

While the program worked and did what it was supposed to do,
there were a couple of things I didn't like about it:

- The name and date were hardcoded into the program,
so they had to be edited if they were to be changed.

- The program wasn't compiled, which made it easy to
edit the name and date, but loading BASIC first
slowed the program down.

- The program did not exit back to the operating system,
leaving the user in BASIC.

- The program only printed one label at a time, which
was okay if you only wanted one label, but was a
pain if you wanted several. I didn't mind that it
would not print multiple copies of a label; but, if
I want to label ten different disks, I don't want to
load and run the program ten times.

Rather than modify the existing BASIC code, I figured I'd write
it in C. (The fact that I didn't have a BASIC compiler had
nothing to do with this decision. 🙂 Besides, I often hear
people just starting out in C ask how to send output to the

A short time later, I had a working program:

Issue C News 11

/* LABEL.C -- Print out a diskette label. Designed for
an ALPS P2000G printer, it should work on any Epson-
compatible printer. */


FILE *printer;

void main()
/* Define arrays to hold each item of data. */
char name[15], title[28], comment1[28],
comment2[28], date[8];

/* Open a file pointer to the printer. */
printer = fopen("LPT1", "w");

/* Prompt the user for each item of data and read
in the data. */

printf("\nPlease enter the title: ");
printf("\nPlease enter a name: ");
printf("\Please enter the date (MM/DD/YY): ");
printf("\nPlease enter the first comment: ");
printf("\nPlease enter the second comment: ");

/* Output the data to the printer. */

fprintf(printer, "\n%s", title);
fprintf(printer, "\n%s %s", name, date);
fprintf(printer, "\n%s", comment1);
fprintf(printer, "\n%s\n", comment2);

/* Flush the buffer and close the file pointer
to the printer. */


All that was left was adding a loop to allow more than one label
to be printed at a time.

First, I decided that I would follow the current trend when
updating software and add lots of "features." Actually, I
realized that here was a simple program that could be used to
demonstrate several things I often hear questions about:

Issue C News 12

- Checking the printer status, is the printer online?

- Sending escape codes to the printer.

- Clearing the screen.

- Getting the system date.

- An example for yorn(). (yorn() is a handy function
for asking yes/no questions which Jaime Dasilva
showed me a year or so ago.)

The compiler used for the code in this program is Turbo C 2.0,
but most functions should compile with little, if any,
modification in any other MS-DOS compiler.

(NOTE: Because not all readers of the C News use MS-DOS as their
primary operating system, several of the following functions
will not apply.)

Sending output to the printer.

Before going into the added "features," let's look briefly
at how LABEL.C sends output to the printer, since this question
comes up from time to time. Basically, just as printf() sends
formatted output to stdout, fprintf() sends formatted output to
a stream (normally stdprn). Both commands accept a series of
arguments which are applied to a format specification contained
in a format string:

printf("\nHello world.");

The difference is that in fprintf(), a stream must be specified:

fprintf(printer,"\nHello world.");

In this example the stream is the printer. Before we can send
the formatted data to the stream, we must open the stream.
Before we can open the stream, we need to declare it.

Wait a minute, what's a stream? A stream is a file or
piece of hardware which is manipulated as a pointer to a FILE
object. To declare a stream, in this case the printer, we

FILE *printer;

In order to open the stream, we use fopen(), specifying the
printer port as the filename and w as the mode:

printer = fopen("LPT1", "w");

Issue C News 13

Once we have finished sending the data to the printer, we need
to make sure that there is no data in the buffer, which we flush
with fflush():


After flushing the buffer, we need to close the stream, which is
done with fclose():


Admittedly, this is not the most detailed explanation of sending
data to the printer, nor is it the only way to do so. However,
this should give you enough background to understand how LABEL.C
works and give you some points of reference for further study.

Checking the status of the printer

Since the people that will be using this program normally
turn their printer on at the same time as they turn on their
computer, since they share a surge protector, I was only
interested in whether the printer was online or not.

Without going into an in-depth discussion of DOS
interrupts, checking the status of the printer can be done
rather easily, by using BIOS interrupt 17, the printer-services
interrupt, function 2. In C, we do this by using int86().

Usually found in dos.h, int86() uses the following syntax:

int int86(int int_number, union REGS *in_registers,
union REGS *out_registers);

and executes the specified software interrupt. Prior to
executing the interrupt, the register values specified by
*in_registers are copied into the registers. After executing,
int86 copies the current register values into out_registers. It
should be noted that in_registers and out_registers can point to
the same structure, which is the case in the following example.

In order to check status of the printer, we need to specify
that we are using interrupt 17 (in hex), giving us:

int int86(0x17, union REGS *in_registers,
union REGS *out_registers);

In order to specify function 2, we must first put 2 into
register AH, which is accomplished by:

regs.h.ah = 0x02;

Issue C News 14

Since we are interested in a printer attached to LPT1 (if you're
not, I am), we need to put the printer number in DX:

regs.h.dx = 0;

(If you want to check the status of a printer on LPT2, the
printer number would be 1.)

After specifying both in_registers and out_registers to be
of the structure specified by the REGS union, all that is left
is to evaluate the returned value AH, returning 0 if the printer
is not ready.

The complete function is as follows:


int prncheck(void)
union REGS regs; /* Declare regs to be of
the structure union
REGS. */
regs.h.ah = 0x02; /* AH = 02 for printer
status */
regs.x.dx = 0; /* DX = 00 for LPT1 */

int86(0x17, ®s, ®s);

return(((regs.h.ah & 0x80) == 0x80) ? 1 : 0);

Sending escape codes to the printer

After checking the status of the printer, sending escape
codes to the printer is very easy. In fact, the hardest part is
often finding the printer manual to look up the appropriate
escape codes.

For example, if we wanted to use elite printing on an ALPS
P2000 printer, the escape codes necessary are 1B and 4D. In
order to send them to the printer, all we need to do is declare
them (in hex) as a character array

char NLQ[] = {0x1B, 0x4D};

and then send them to the printer

fputs(NLQ, printer);

That's all there is to it.

Issue C News 15

Clearing the Screen

There are several ways to clear the screen, in fact, most C
compilers have a function to clear the screen in one of their
libraries. Assuming ANSI.SYS is present, we could send \xB1[2J
to the screen to clear it. An easier way would be to send a
series of carriage returns/linefeeds to the screen. All these
methods will clear the screen, but what it we only want to clear
a portion of the screen? By using the video BIOS scroll-window
function, interrupt 10, function 6, we can use int86() to clear
the screen or a portion of it. That's why rather than hard code
a screen size into this function, I have chosen to have the
values for the number of rows and columns in the screen to be
passed to the function.

Wait a minute . . . "scroll-window," isn't that the same as
sending a series of carriage returns/linefeeds? Well, I admit
it, it is very close. However, this screen clearing function
can be used to clear just a portion of the screen, as it
specifies the area to clear.

AL contains the number of lines to scroll. If AL is equal
to 0 or is greater than number of rows specified by DH, the
screen is blanked.


void clrscr(rows, columns)
int rows, columns;
union REGS regs;
regs.h.ah = 6; = 0; /* Number of lines to
scroll. */ = 7; /* Attribute to use for the
screen. */ = 0; /* Row, upper left corner. */ = 0; /* Column, upper left
corner. */
regs.h.dh = rows;
regs.h.dl = columns;

int86(0x10, ®s, ®s);

One problem with clearing the screen in this manner, is that the
cursor is left at the lower right corner of the screen. Using
the BIOS cursor-position function, interrupt 10, function 2, and
int86(), we can specify the row in DH and the column in DL and
reposition the cursor wherever we want it.

Issue C News 16


void set_cursor(rows, columns)
int rows, columns;
union REGS regs;

regs.h.ah = 0x02; = 0;
regs.h.dh = rows; /* Return cursor to this row. */
regs.h.dl = columns; /* Return cursor to this column. */

int86(0x10, ®s, ®s);

A faster, simpler way to clear the entire screen would be to use
interrupt 10, function 0, the BIOS set video mode function. Of
course, you must know the display mode value for your system,
which goes in AL. For a 16 color EGA display adapter, connected
to an EGA monitor, with a pixel resolution of 640 x 350, a
character display of 80 x 25, the value for AL is 2.


union REGS regs;

regs.h.ah = 0; = 2; /* Display mode. */
int86(0x10, ®s, ®s);

Getting the date

Well, we've been using interrupts for everything else, so
it should not be a surprise that we will use an interrupt to get
the system date. DOS interrupt 21, function 2A, will return the
date of the DOS internal clock. (NOTE: The DOS internal clock
is the clock that can set by the operator with DATE and/or TIME
in AUTOEXEC.BAT or at any DOS prompt.)

In the previous functions where we have used the DOS
interrupts, we have used int86(); but to get the system date, we
will use intdos(). (We could use int86() just as easily.)

While int86() is a general 8086 interrupt function,
intdos() is a general DOS interrupt interface. Rather than have
to specify interrupt 21, intdos() automatically accesses the DOS
interrupt 21. Other than not having to specify the interrupt
number, intdos() works just like int86().

Issue C News 17

The DOS interrupt 21 function to get the system date is 2A,
so we have to set the value of AH to 2A.

Unlike our previous functions, the system date will be
returned in three registers, DH, DL, and CX. (If we wanted the
day of the week, it would be returned as AL.) The numeric value
for the month (1 through 12), is returned in DH. The day is
returned in DL. The year, from 1980 to 2099 is returned as CX.
In order to write these values to a string, we use sprintf().
(Because we want the date in MM/DD/YY format, we need to
subtract 1900 from the value in CX.)


char *get_date()
static char date[9];
union REGS regs;

regs.h.ah= 0x2a;
intdos(®s, ®s);

sprintf(date, "%02.2d/%02.2d/%02.2d", regs.h.dh,


Yes or no answers with a default: yorn()

Okay, enough messing with interrupts. The need often
arises for a program to ask a yes or no question. In many
cases, it would be nice to have a default answer where the user
just has to hit enter. There are a number of ways to do this,
but my favorite is yorn(), which Jaime Dasilva posted on the C
BBS about a year or so ago. It's not an earth shaking function,
but it is the answer to an often asked question and it makes
asking a number of such yes/no questions much easier.

What makes yorn() nice to use is that the question and the
default answer are passed to the function. So, while yorn() has
expected lines

if(c == 'Y' || c == 'y')
return YES;
else if(c == 'N' || c == 'n')
return NO;

you don't need to repeat them every time you have to ask a yes
or no question which has a default answer. There is nothing

Issue C News 18

difficult about yorn(), as can be seen by the following code,it
is fairly self-explanatory.

/* yorn.c - yes/no prompting, with default. */


#define YES 1
#define NO 0
#define LINELEN 129 /* DOS max line length + 1 */

int yorn(char *prompt, int dfault)
char s[LINELEN], c;
int len;

printf("%s (%c) ", prompt, dfault ? 'Y' : 'N');
fgets(s, LINELEN, stdin);
len = strspn(s, " \t\n"); /* skip whitespace */

if(len < strlen(s))
c = *(s + len);
if(c == 'Y' || c == 'y') return YES;
else if(c == 'N' || c == 'n') return NO;

return dfault;


The original C version of this label program (above), was
named LABEL.C, why did I change it to BLOB.C? Because it kept
growing and growing.

The revised code for BLOB.C is included in the archive with
this file. Most of the code should be self-explanatory by now.

BLOB is not meant to be a tight, small label program.
There are a lot of things that could be done differently. BLOB
serves as an example of a few handy little functions, functions
which answer some commonly asked questions.

Issue C News 19

Beginner's Column by Wayne Dernoncourt

This column is going to deal with structures, more
specifically with data structures. In a building, a structure
is an object composed of one or more parts with a common purpose
of being a building. Different types of structures will have
variations in the parts, but they all contain the same basic
parts (i.e.. roof, walls, doors, windows, etc.). If you
duplicate a structure, the new one will have the same pieces as
the one that you duplicated. If you go into a modern apartment
complex, most will have three or four floor plans for their
various apartments. All apartments share components with other
dwelling units, from townhouses to mansions, -- all have
bathrooms, kitchens and bedrooms, etc. These components can be
thought of as different types of data elements (ints, floats,
chars, etc.). Just as one apartment building has multiple
dwelling units, an apartment complex can have multiple apartment
buildings. One of the data elements that a structure can
contain is another structure (of course you can't have a
circular reference).

A data structure can be thought of in the same way.
Different data structures are composed of different variables.
The entire data structure is located in contiguous memory. If a
data structure is composed of 2,000 bytes, those 2,000 bytes of
memory will be located in one piece of memory. This is a
requirement imposed by the ANSI standard. Unfortunately this
usually has the side effect of imposing a restriction on IBM PC
programs on the size of data structures. Data structures can be
made up of integers, floats, characters or structures or arrays
of any of these or any combinations of these.

In a C program, there are two methods of accessing the data
in a structure. This article will deal with the simpler of the
two, while next issue's article will discuss the more advanced

To illustrate this concept, consider the following
requirement: You have to write a program that will read a series
of files and perform the following editing functions:

1) Turn IBM special characters into blanks (printers don't
usually handle these characters very well)
2) Drop trailing blanks from lines (sometimes the actions from
the first step will result in a line of nothing but blanks
3) Print the resulting line to a file

Issue C News 20

Given these requirements, let's write an algorithm to solve this

1. GET information about filetypes that we're interested in
2. OPEN output file - check for success
3. AS long as the request for info on good filetypes is filled:
3.1 OPEN the file for reading - check for success
3.2 WHILE not at EOF
3.2.1 RESET the input buffer
3.2.2 READ from the file into the input buffer CHECK for special character and
translate if needed
3.2.3 TRIM the line of trailing blanks
3.2.4 PRINT the line to the output file
3.3 GET info on the next file for reading
3.4 CLOSE the current input
4. CLOSE the output file
5. END the program

Translating this algorithm into a program, here is the actual
code that is used:

This program is supposed to read all files with a filetype of
TST, convert all of the non-standard ASCII characters (all
characters not in the range of \x20 to \x7f to a space, all
trailing blanks are then to be eliminated with the resulting
line written to an output file.

/* prototypes go here */
int read_line (char *in_line);
void trim_line (char *in_line, int length);

/* include files go here */

/* Global variable declarations go here */
FILE *infile, *outfile; /* For some reason,
I have better luck
with files as global
variables. */
/* Local variable declarations */
char in_line[150];
int length;
int found_file;
struct ffblk fileinfo; /* This declares a STRUCT */

Issue C News 21

/* find the first file to read */
found_file = findfirst ("*.tst", &fileinfo, 0); /* 1. */

/* open the output file */
if ((outfile = fopen("test.out","wt")) == NULL) /* 2. */
printf ("I can\'t open the output file\n");

/* while files are present to process */
while (!found_file) /* 3. */
/* 3.1 */
if ((infile = fopen(fileinfo.ff_name,"rt")) == NULL)
printf ("I can\'t open the input file\n");

while (!feof(infile)) /* 3.2 */
memset(in_line, ' ', 150); /* 3.2.1 */

length = read_line(&in_line[0]); /* 3.2.2 */

trim_line(&in_line[0], length); /* 3.2.3 */

fprintf (outfile, "%s\n", in_line); /* 3.2.4 */
found_file = findnext(&fileinfo); /* 3.3 */
fclose(infile); /* 3.4 */
fclose(outfile); /* 4. */
} /* 5. */

Issue C News 22

int read_line(char *in_line)
This function returns the line read in from the input file.
Modify the input character string so that all fall into the
range of printable ASCII characters. The only character
excluded is the new-line character. All non-conforming
characters are translated into blanks.
char unsigned in_char='\x00';
int index = 0; /* index is the character
position in the return buffer,
it's the length of the buffer */

while (in_char != '\n' && !feof(infile))
fscanf(infile, "%c", &in_char);

if ((in_char < ' ' || in_char > '\x7F') && in_char != '\n')
in_char = ' '; /* */
in_line[index] = in_char;
in_line[index] = ' ';


void trim_line(char *in_line, int length)
This functions returns a line trimmed of all trailing blanks
int index;
for (index = length; in_line[index] == ' ' && index >= 0; index--)
in_line[index] = '\x00';

In previous articles, you've already been introduced to
using files. Whenever you open a file, the system fills a
structure with data regarding this file. The FILE macro
(defined as part of the stdio.h header) creates this data
structure. Another data structure is created by the use of
struct keyword, in this case the type is called ffblk and I
called the data structure instance fileinfo. The template for
the ffblk structure is found in the dir.h header file.

Issue C News 23

I initially fill the fileinfo data structure with
information using the function findfirst (see line 1). This
function returns a zero if no files were found that matches the
search criteria (in this case all files with a file type of
TST), otherwise it returns a number (what the number is doesn't
matter). Notice that the data structure is passed to the
function with the '&', this instructs the compiler to pass the
address of the structure to the function. I assign the value
returned by findfirst to a variable called found_file. I update
the fileinfo data structure on other files using the findnext

As long as found_file is not zero (see line 3), I open the
file that was found by using the ff_name offset of the data
structure. This could be confusing to some people, let me stop
and try to explain this a little more. The fileinfo data
structure is composed (in this instance) of a 21 character array
reserved for DOS, a 1 byte character that is the file attribute
(i.e., read-only, hidden, system, etc.), 2 unsigned integers
that contain the date and time fields, a long integer that
contains the length of the file and a null-terminated character
array containing the file name. When the program is run, this
data structure initially contains undefined data. This data
structure changes to reflect data on the files that we're
interested in. This data is updated by using the findfirst and
findnext functions. The findfirst function fills out the data
structure initially and the findnext function gets information
on later entries. (see line 3.3)

Next month, another way of accessing data structures.

Issue C News 24

Where's That Number? by Jim Singleton

A Project In Programming Design (Revisited)

Last issue, we looked at one of the textbook methods of
programming design. While the seven steps involved were covered
in order and in sufficient depth to understand the processes
involved, I have to apologize for the article. Due to a variety
of reasons, most notably that I was leaving the country the day
the article was due, the code accompanying the article was not
as good as it should have been. As published, PHONENUM.C will
work. While I have a variation of PHONENUM.C, I felt it needed
to be modified slightly for general use. At the same time,
there were things which I didn't change, which I should have

Looking back at PHONENUM.C, what's wrong with the code? As
you may remember, I mentioned one bug in the article, which is
was using ASCIIZ strings with no initialization. (If you missed
this one and found others, you're not alone.) This causes
garbage characters in the database, which results in different
output files every time you add names to the database. This
makes it virtually impossible to use a comparison of the
database as a means of testing variations to the program.

A second glaring error, which I didn't notice until,
Charles Falconer pointed it out, was the use of fixed fields.
As Charles correctly pointed out, this can "lead easily to nasty
bugs, such as the use of 'gets' in [the] program and the
associated C insecurities." (Luckily, Charles, I have not yet
been bitten by this bug.) In addition, in the case of the field
for the phone number, it doesn't allow for extensions, overseas
numbers, etc. (As one can tell, I have never put my fiancee's
phone number, in Ireland, into the program.)

Are there other errors? Yes and no. As I have mentioned,
the program will work and does do what it was designed to do.
In fact, since this seems as if it is becoming an ongoing series
of articles, we can easily make modifications to the code with
each installment. However:

- I overlooked the fact that the program requires the
user to correctly enter the exact name of the person
whose phone number they are retrieving.

- I also did not allow any means for checking to see
what numbers are already in the database. (Although
this was considered for a later article, it is a feature
that should be present at the outset.)

Issue C News 25

- I never did consider the size of the program, which
is rather large for such a limited number of features.
(Charles said this best, as "the whole thing seems to
be squashing mosquitos with a pile driver.")

I received a number of proposed modifications to
PHONENUM.C, ranging from several lines to completely new
programs. Charles Falconer did not care for the interior design
of PHONENUM.C, so he rewrote PHONENUM.C, using a more modular
approach. While I have seen this approach before, I appreciate
it much more after taking a crticial look at my code from issue
18. Charles' formatting and break-down philosophy results in
self-documenting code, with modules which are small and of a
single purpose. Charles' version of PHONENUM is included in
this archive in the file FONENUM.C. Thanks, Charles.

What's next? The obvious shortcomings of the program are
listed above, so anyone that wants to improve PHONENUM.C can
either use them as a guide or look at Charles' program.
PHONENUM.C is a simple program, so it doesn't really lend itself
to some of the "features" discussed in the last issue; however,
it will probably appear from time to time as a platform for
demonstrating different functions.

Issue C News 26

Portability Coding Issues

When I first started working in an environment where I was
using multiple C compilers I assumed that given the ANSI C
standard, writing C code that was easily portable between the
compilers would present little or no difficulties. My favorite
environment to develop code in is Turbo C. I thought that I
could tweak all the ANSI C flags on the TC compiler, write the
routines, test them with some drivers and then move the source
to the target systems and everything would be fine. This idea
turned out to be naive more often than not. After a few trials
it became obvious that ANSI C by one compiler's definition ain't
necessarily ANSI C by another's. Plus, it turns out that there
are a lot of things that the ANSI C standard does not mandate
but leaves up to the implementation. To avoid the situation
where I would be maintaining multiple versions of the same
source file, I needed some guidelines that would allow me to
write more portable code.

For my purposes I defined 'portable C code' as code that
can be compiled and run on multiple operating systems and
hardware platforms, with no source modifications, and produce
identical results (or at least acceptably similar, taking into
account different data representations). This would seem to be
an obvious blanket definition but I thought that I had better
give myself an ideal situation to shoot for.

Exceptions would be (there are always exceptions):

1) Low level routines that can be non-portable because of
unavoidable machine dependence. (i.e. mapping of
hardware or operating system data structures)

2) Major differences in compiler implementations.

In the case of machine dependent routines, I decided that I
could isolate these into Machine Dependent Libraries. In the
case of multiple compilers I could stick with the lowest common
denominator to eliminate annoying 'minimal' code changes.

My approach was to identify native C operations and common
coding assumptions that could produce non-portable code,
regardless of compiler adherence to the ANSI C standard. These
are listed in this article. There are complete volumes written
upon portability and the C language and I make no claim that
these guidelines cover all aspects of portability. They do
however provide good 'rules of thumb' and I have found them
useful in minimizing my portability problems. I used a number

Issue C News 27

of reference books while compiling this list and I can recommend
the following for further reading:

o C:A Reference Manual Second Edition, Samuel P. Harbison
and Guy L. Steele, Tartan Laboratories, Prentice-Hall
Software Series 1987

o Portability and the C language, Rex Jaeschke, Hayden
Books - C Library 1988

o The Waite Group's Essential Guide to ANSI C, Naba
Barkakati, Howard W. Sams & Company 1988

If you are reading this you are assumed to have a working
knowledge of C, its data types and constructs. This document
does contains some discussion of binary and unary conversions,
trigraphs and character escape sequences. When applicable,
acceptable alternatives to the operations discussed are
specified. A limited explanation is usually given to avoid
making arbitrary do or don't statements.

ANSI C is referenced where appropriate and when the
operation involves ANSI C extensions. However, if you are using
multiple compilers, refer to their specific documentation to
determine what is acceptable to all of them. If that happens to
be an ANSI C construct, great!!

For reference, the systems that I have been working with

1. IBM PC/RT running AIX 2.2.1
2. IBM 4381 running VM/XA SP2 using C/370
4. PS/2 60 with DOS 3.3 and TURBOC 2.0

Bit Fields

Due to differences in byte ordering, bit field widths and
alignment restrictions on different machines, code using bit
fields will likely be non-portable. Use of bit-fields should be
restricted to to situations where hardware defined data areas
must be matched exactly.

C specifies that no compiler has to allow bit fields of any
type except unsigned, but some compilers do allow signed (that
is sign extension occurs when extracting the contents). Use of
any type other than unsigned for bit fields is considered less
portable than using bit fields at all.

Issue C News 28

Bit Wise operations

Due to different implementations of signed integer numbers,
the bitwise operators: ^, |, and ~ should NOT be used on signed
values. Likewise the shift operators >> and << should NOT be
used on signed operands.

ANSI C mandates that integers use binary encoding to permit
portable bitwise operations and shifts upon POSITIVE integers.
If the contents of an integer could become negative, bitwise
operations should not be used as results will be unpredictable
when ported. Casts may be used to cast operands to be

Byte Order and Alignment

Routines coded that assume a particular byte order will not
be portable. Likewise, routines that assume specific data
alignment will not be portable.

Addressing may be right-to-left or left-to-right.
Alignment restriction may not exist, may be strictly enforced or
in some cases adjustable by the hardware or implementation
depending upon the data type referenced. Run time errors or
unpredictable results may occur if addressing and alignment
conditions are assumed.

Numeric Escape Codes

Numeric escape codes should not be used for two reasons.
They tend to hide the meaning of the code in a cryptic mass of
numbers and they are dependent upon the implementation's
character set. If the specific encoding is not present in the
character set, results will be unpredictable. Also there is an
implicit assumption that characters will be represented in eight
bits. This is not portable either. Escape codes should be hid
in MACRO definitions both for code clarity and isolation.

Character Constants

Character constants are written by enclosing a character in
apostrophes and have type 'int'. Most computers contain
integers in a storage area large enough to contain several
characters and thus some C implementations allow multi-character
constants such as 'ABC' creating an integer value (not a string,
no '\0' ending character). There are implicit assumptions about
byte-ordering involved in the use of such a constant, so the
actual value is non-portable.

Issue C News 29

ANSI C allows multiple character constants but their actual
value is left up to the implementation. Note that
multi-character constants are not the same as multi-byte

(See previous discussion on byte ordering)

Character Definitions

All character definitions should be defined as 'unsigned'
for consistency and portability. Different implementation may
have default characters as signed or unsigned or even
pseudo-unsigned (treated as signed when performing the usual
unary conversions but the actual value cannot be negative). If
left as an implicit definition, unpredictable results could

Character Escape codes

Some character escape codes are defined to be independent
of the computers character set. The appear in code preceded by
a back slash. (e.g.'/n'). The character escape codes that are
allowed by the ANSI standard are listed here:

a produce an audible alert (ANSI extension)
b backspace
f form feed
n newline
r carriage return
t horizontal tab
v vertical tabulate
\ backslash
' single quote
" double quote
? question mark
Interpretation depends upon printer or terminal
x Interpretation depends upon printer or terminal

Some compilers may or may not support all of the above
sequences. Check the compiler documentation.

Character Sets

ANSI C introduces trigraphs so that C programs may be
written in a subset of ASCII. For example the character
trigraph ??) will represent the ASCII character ].

Issue C News 30

I personally find that trigraphs are a pain to use and they
make for really ugly code. However when porting C source to a
machine which does NOT support certain ASCII characters the
trigraphs must be used. The port procedure must be responsible
for converting the necessary ASCII characters to their trigraph
equivalent. For example, EBCDIC machines do not support the
characters [ and ]. If the C source is coded on an ASCII
machine and ported to an EBCDIC machine, the characters [ and ]
must be translated to their trigraph equivalents ??( and ??)

If the target systems compiler does not support trigraphs
or the required characters, MACROs should be defined for the
appropriate escape sequences. I have seen this done
successfully in the old ORACLE C compiler for IBM 370 machines
before C/370 blotted that landscape.

The translation of trigraphs in the source program occurs
before lexical analysis (tokenization) and even before the
recognition of character escapes introduced with a backslash \.
Only these exact nine trigraphs are recognized.

Trigraph ASCII Character
-------- ---------------
??( [
??< {
??/ \
??' ^
??= #
??) ]
??> }
??! |
??- ~


Comments are defined as beginning with /* and terminating
with */. Nested comments are NOT ANSI standard although some
compiler implementations do allow it. If a section of code is
to be suppressed and the section contains comments, preprocessor
statements should be used:

#if 0

Issue C News 31

rather than:


Data Type Sizes

The exact size of the various data types for an
implementation is usually defined in the limits.h and float.h
header files. This is ANSI C and most other compilers follow it
although other functions and MACROs may also be present. Check
the individual compilers files before making any assumptions as
to the size of a data type.

See discussions of floating point and integer types.

External names and internal identifiers

The C language allows and is sensitive to mixed casing in
identifiers. Early versions of C specified 8 significant
characters in identifier names. ANSI C requires implementations
to allow 31 significant characters and some allow more. Again,
use the lowest common denominator if using multiple compilers.

Although C permits long external identifiers, the host or
target operating system may require shorter names. All external
name should be encapsulated through the use of pre-processor
statements. This allows meaningful names to be coded and
permits maximum portability.

#define tut_error_handler tut00u

extern void tut_error_handler();
int *p;

if (!p) tut_error_handler("Null pointer error");

Floating Point types

There are no requirements in C implementations about the
relative sizes of the floating-point and integer or pointer
types. Floating point number representation is completely
machine dependent. It is generally assumed that the type
'double' will be large enough to accurately contain all value of
type 'long'. While this is likely to be true, C does not
require it and any code written should avoid depending on this

Issue C News 32


The 'long double' type give C program access to an extended
precision floating point values. However, implementation are
free to make 'long double' the same size as 'double' as they are
free to make 'double' and 'float' the same. Check the
individual compiler documentation before making any assumptions
on floating point sizes.

ANSI C allows floating arithmetic to be done without
'widening' of the operands.

Function Pointers

Function pointers must be explicitly typed or the results
can be unpredictable. Implementations can use different sizes
for data and function pointers. Assuming the default 'int'
return type is 'good enough' is not good enough.(See discussion
of pointers)

Input and Output

All input and output (I/O) in C programs should be done
using C's standard I/O facilities. These are based upon the
concept of a stream which may be a file or a device. A file
pointer is returned to the application from the function fopen
and is used as an argument to most of the I/O facilities. Calls
to host operating system utilities to perform I/O are not
allowed (or portable).

Integer Arithmetic

There is not much that can be done about predicting run
time divide by zero other than to check for a 0 divisor at every
computation (which may incurr unacceptable overhead). It should
be noted here that the results of an overflow of a signed
integer type are unpredictable. An incorrect value may be
produced, the program may terminate execution or some
machine-dependent trap or exception may occur. This holds for
>> or << operations whose right hand operand is too large or
negative as well.

Unsigned integer overflow results are well defined by C,
namely, the result is computed using modular arithmetic, but
this is dependent on the representation of int. Routines should
never be coded to take advantage of any action occurring in the
case of a signed overflow and dependenc on the modular nature of
unsigned overflow should be avoided.

Issue C News 33

Integer Types

Integers come in three types: short, int and long. Int may
not be shorter than short and long may not be shorter than int.
However it is permitted for short and int to be the same size or
for int and long to be the same size. Of the three, int is the
default integer type and thus is likely to be the most efficient
in a given implementation. Unfortunately it is also the least
portable being implemented in 32 bits by some compilers and 16
on others. A long type integer is likely to be implemented in
32 bits as it can give the largest range of integer values.
Operations on type long may be slower than operation on type

The choice of what integer type to use comes down to
portability or efficiency.


'K & R' C did not allow initialization of automatic
aggregates (arrays). ANSI C does. I have found that if a
compiler does not follow a complete ANSI C implementation, this
is not likely to be supported. Check the appropriate compiler


Substitution of macro parameters into string constants and
character constants is up to the implementation of the
compiler. For example the definition:

#define MAKESTRING(x) "x"

used in the following statement:

MAKESTRING( a += 1 /* Comment */)

could result any one of the following:

"a += 1 /* Comment */"
"a += 1 "
"a += 1 /**/"
"a+=1/* Comment */"

ANSI C does describe the # and ## operators to be used for
'stringization' and 'token merging' however check with the
appropriate compiler documentation to see what is supported and
be sure sure you know what you are trying to achieve.

Issue C News 34


Pointers should be initialized to null or to their desired
run time value when they are declared.

int foo;
int *p = NULL;
int *q = &foo;

Some implementation will give compile errors if a pointer
is not initialized before use, other will go ahead and use
whatever is in the pointer. Defining a pointer to null should
force some sort of run time "null pointer assignment message" if
the pointer has not been properly defined prior to use.

Assigning integers to pointers should not be done except in
the special case of assigning a constant zero to initialize a
null pointer. Note that the null pointer concept does not
necessarily mean an all-bits-zero value.

Proper type casts must be used when assigning pointers to
pointers of different types. This will guarantee portability
and conversion without error.

long number;

long *lng_ptr = &number;
char *string;
string = (char*)lng_ptr;

Register Variables

You should not use the unary operator '&' upon a register
variable. Some implementations may support this as register is
only a hint to the compiler and not a mandatory requirement, as
well as the fact that on some computers, registers are
addressable as if they were storage locations. On the other
hand if a register is available and used, the & operator would
force it to be auto instead defeating the whole purpose of the
register declaration. Some compilers will issue warning
messages but this should not be counted upon.

Issue C News 35

Program return codes

Do NOT rely upon the return code of a program being that
which is specified in the return statement. Use the exit
library function instead.

ANSI C does mandate that an explicitly returned value from
main will be the programs' exit code.

Switch Statements

Do NOT rely on a switches case being resolved in the order
they are specified. The order of case testing is undefined and
depending upon the set of case values, and their contiguity, the
implementation may test in a variety of ways, even within the
same switch.


The only time a component of a union may be addressed is if
the last assignment to that union was through the same
component. It is not portable to assign one union component and
then reference another. Some compilers will allow this, others
will not compile or give an run time failure.

union U { long c; double d; } x;
long l;

x.d = 1.0e10;
l = x.c; /* not portable */

Usual unary and binary conversion performed on operands

Unary Conversions: applied to ^, | ,~ , <<, >> and function

Original operand type Converted Type
--------------------- --------------
char, short int
unsigned char unsigned
unsigned short unsigned
float double
"array of T" pointer to T
"function returning T" pointer to function returning T"

Binary Conversions are done before Binary or Ternary Operations
are performed. An operator that performs the usual binary
conversions on its two operands, first performs the usual unary

Issue C News 36

conversions on each of the operands independently. The usual
binary conversion as defined by ANSI C are:

1. If either operand is not of arithmetic type, or if the
operand are the same type, no conversion are performed.

2. Otherwise, if one is of type long double then the other is
converted to type long double.

3. Otherwise, if one is of type double then the other is
converted to type double.

4. Otherwise if one operand is type float the other is
converted to type float.

5. Otherwise, if one is of type unsigned long int then the
other is converted to unsigned long int.

6. Otherwise, if one operand is of type long int and the other
is of type unsigned int, and a long int can contain all unsigned
int values, the unsigned int is converted to long int otherwise
both operands are converted to unsigned long int.

7. Otherwise, if one operand is of type long int the other
(which must now be type int) is converted to type long int.

8. Otherwise, if one operand is of type unsigned int then the
other is converted to type unsigned int.

9. Otherwise, both operands are of type int and no other
conversions are performed.

These are similar to those defined by K&R except for expansion
for long double and unsigned long.

Some compilers convert type float in function argument lists to
type double and some will supply an option to suppress this
conversion. Check the compiler documentation.

Issue C News 37

New PD/Shareware Releases by Barry Lynch

Listed below are some of the new programming related
shareware or public domain utilities, libraries and code
fragments that have been released over the last few months.
Opinions expressed are of the author only. The files listed
below are available from the Bulletin Board Systems that are C
News Distribution points.

Filename: MEXT17.ZIP

The Microsoft Editor comes free with the C compiler and the
MASM assembler. One of the capabilities of the editor is the
ability of the programmer to write editor extensions in C. This
file is a collection of extensions written by Fridtjof Weigel.
The one limitation of this collection is that they were compiled
with OS/2 extensions.

Therefore, if you want to use them with the DOS based
Microsoft Editor you are out of luck, unless you modify the
source that is distributed. Listed below are some of the
capabilities that are available with this extension library.

- funclist: Builds a list of functions
- keymacdef: Begins a key macro
- keymac: Executes a keystroke macro
- indent: Indent a range of lines
- reflow: Wordwraps a paragraph

Filename: C_MENUS1.LZH

This file contains a menuing system written in TurboC v2.0
by Karl Keyte of West Germany. A library of functions, header
file and a reference file are included in the archive. The
library contains functions to define a menu (define_menu),
display (display_menu), remove (remove_menu) and various
others. A sample program outlining the usage of the functions
is not included in the archive. However, Karl will try to
assist if you are willing to call him or send him e-mail or a
letter. Karl states in the documentation that the source may be
available depending upon whether the source is undergoing major
modifications or not.

Issue C News 38

Filename: MABBRE.COM

This archive is another set of extensions for the Microsoft
Editor. These extensions implement templates for various C
language keywords. For example: If you type in "w", and press
Alt-tab (access the extension) the result will be "while ()".
Setting up CABBRE (C language vs. PABBRE - Pascal also included
in the above archive) involves copying the CABBRE.MXT file to
the same directory where "Tools.ini" resides, and adding the
following line to the "Tools.ini" file. "Load::CABBREV.MXT".
The extension works as advertised and the source is included
with the archive.

Filename: STABV1.COM

This is another extension for the Microsoft editor. Stab
stands for "Smart Tab". The smart tab has two features:
awareness of the insert mode, and the ability to to set variable
spaced tabs. To load STAB, add the following line to your
"Tools.ini" file. "Load::STAB.MXT". The source is also
included in this archive.

Filename: MCURFI.COM

And finally, another Microsoft Editor extension. This one
reconfigures the cursor. This is especially helpful when using
the M editor in the 43 line mode. As with the other extensions
mentioned previously, the source code is included in the

Issue C News 39

Article Submission Standards

All articles, reviews and letters to editor should be
submitted as ASCII files. Please do not format the text in any
way (no hyphenation, justification, indentation, etc.) since we
use Proff to format C News. Proff takes care of the
justification, footnotes and headers.

You can send in your article on a diskette or send it
electronically to one of the editors. See "How to Contact us
here at C News" for more information.


Issue C News 40

How to get hold of us here at C

You can reach us via Snailmail at:

The C News
P.O. Box 15314
Arlington, VA 22215

and electronically via the following electronic mail addresses:

FidoNet: 109/138.1
CIS: 71540,2677

Fidonet: 1:124/3116
North Texas C Programmers Board: 1-214-442-0223
MCI Mail: 272-8067

Internet: [email protected]
UUCP: ...!uunet!mimsy!dbk

(Rob Koel)
European C News Distribution Point: 011-3185642891

 December 8, 2017  Add comments

 Leave a Reply

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <s> <strike> <strong>