[comp.lang.c] Unbuffered I/O using MicroSoft C 3.0

ugwayne@sunybcs.UUCP (01/09/87)

Subject: Unbuffered I/O using MicroSoft C Version 3.0
-----------------------------------------------------


    A friend of mine recently asked me "If I knew of a program that would let
your printer act like a typewriter?".  Since a program of this sort seemed 
really easy to write (just get characters and send them to the printer), I
decided I would just write one myself.  Well, I am sorry to say, things have
not been as easy as I thought they would be.  The problem is that I can't get
the printer to just print one character at a time.  It only prints out a line
at a time.
    I am using MicroSoft C 3.0 and I have tried various methods but each time
the printer only prints out when I enter a Carriage Return.  At first, I was
using getc() and putc() and then it dawned on me that these functions
use buffered I/O.  So I looked in the MicroSoft manuals and found the 
function setbuf.  I tried this on the stream stdprn and then ran the program
again.  It still didn't work.
    Next, I used the functions read() and write() which the manual states as
low-level I/O that do not buffer or format data.  But again, the program
will not work the way I want it to.  The Carriage Return is still needed to
start the print.  Then, I thought that it might be that the printer was getting
the characters one at a time (so the pogram was working) and it may need a
Carriage Return before it does any printing (it had its own buffering).
    With this in mind then I decided to just try my program with the standard
input and output devices.  So when I hit a key, I would not only see my 
keystroke but also another one which my program was then sending.  This too, 
did not work, a Carriage Return was still needed. 
    So what I would like to know then, Is how would I get a program like
this to work?  How do you get unbuffered I/O using MicroSoft C 3.0?
What am I doing wrong?  If anyone has any suggestions, I
would sure appreciate a response.  This is really driving me crazy.   

                             Thanks in advance,

                                   Wayne


     


     Wayne Nelligan
     SUNY at Buffalo Computer Science


-------------------------------------------------------------------------
.{bbncca,decvax,dual,rocksvax,watmath,sbcs}!sunybcs!ugwayne
CSNET:    ugwayne@Buffalo.CSNET
ARPANET:  ugwayne%Buffalo@csnet-relay.ARPA
BITNET:   ugwayne@sunybcs.BITNET

larry@kitty.UUCP (Larry Lippman) (01/09/87)

In article <1867@sunybcs.UUCP>, ugwayne@sunybcs (Wayne Nelligan) writes:
>     A friend of mine recently asked me "If I knew of a program that would let
> your printer act like a typewriter?".  Since a program of this sort seemed 
> really easy to write (just get characters and send them to the printer), I
> decided I would just write one myself.  Well, I am sorry to say, things have
> not been as easy as I thought they would be.  The problem is that I can't get
> the printer to just print one character at a time.  It only prints out a line
> at a time.
>     I am using MicroSoft C 3.0 and I have tried various methods but each time
> the printer only prints out when I enter a Carriage Return.
> [further discussion of C I/O functions]

	The problem you relate has nothing to do with C programming or the
nature of your operating system - it strictly pertains to the functional
characteristics of your printer.
	Many electromechanical printers using wire-matrices, bands, chains
printwheels, etc. can only print the line buffer all at once (this is an
inherent printer design characteristic).  Most "better-quality" printers
of the above genre will permit "overprinting" of a line; i.e., printing
a line buffer without advancing the paper.  Comparatively few printers
(that are also NOT KSR terminals) will truly print one character at a time.
	To cite a few examples from looking around my lab:

1.	AT&T/Teletype 40P202 (132 col 300 lpm chain printer):
	<CR> prints the line buffer without advancing the paper
	<NL> prints the line buffer with advancing the paper
	NO single-character printing

2.	Okidata Microline 82A (120 cps wire-matrix printer):
	<CR> prints the line buffer with advancing the paper
	<NL> advances the paper one line but prints nothing
	NO single-character printing

3.	Centronics Horizon H136A (120 cps wire-matrix printer):
	<CR> prints the line buffer without advancing the paper, provided
		that an option switch is set to disable paper advance on <CR>
	<NL> prints the line buffer with advancing the paper
	NO single-character printing

3.	Centronics 703 (120 cps wire-matrix printer):
	<CR> prints the line buffer without advancing the paper
	<NL> advances the paper one line but prints nothing
	NO single-character printing

	Note the different characteristics of the above printers.  By far,
the Okidata is the "cheapest" of the above printers, and there just ain't no
way to get it to print other than a whole line along with advancing the paper.
	Now you should see where your problem lies.  There may be a way
around the problem under some circumstances - keep overprinting, with each
successive overprint using space characters to force the desired character
into proper position.  However, this does seem to be rather extreme.
	There is an important moral to what may seem like a trivial
discussion:  Depending upon your printer and its configuration, simple C
statements directing output to a printer (i.e., though a stream defined
as a printer using fputc, fputs, fprintf, etc.) may NOT do what you think
they should!

<>  Larry Lippman @ Recognition Research Corp., Clarence, New York
<>  UUCP:  {allegra|bbncca|decvax|nike|rocksanne|watmath}!sunybcs!kitty!larry
<>  VOICE: 716/688-1231        {hplabs|ihnp4|mtune|seismo|utzoo}!/
<>  FAX:   716/741-9635 {G1,G2,G3 modes}    "Have you hugged your cat today?" 

tom@uw-warp.UUCP (Tom May) (01/12/87)

In article <1867@sunybcs.UUCP>, ugwayne@sunybcs (Wayne Nelligan) writes:
>     I am using MicroSoft C 3.0 and I have tried various methods but each time
> the printer only prints out when I enter a Carriage Return.  At first, I was
> using getc() and putc() and then it dawned on me that these functions
> use buffered I/O.  So I looked in the MicroSoft manuals and found the 
> function setbuf.  I tried this on the stream stdprn and then ran the program
> again.  It still didn't work.

What's going on is that your terminal input is line-buffered so you can use
backspace, function keys, etc.  A setbuf on stdin won't turn off this
buffering, since it is done by MS-DOS before the C library gets hold of
your characters.  You have to invoke MS-DOS directly, as demonstrated below.

>     Next, I used the functions read() and write() which the manual states as
> low-level I/O that do not buffer or format data.

That may be true, but the very-low-level I/O routines, i.e., MS-DOS, may still
buffer stuff.  And in fact they do, at least as far as terminal input is
concerned.

> Then, I thought that it might be that the printer was getting
> the characters one at a time (so the pogram was working) and it may need a
> Carriage Return before it does any printing (it had its own buffering).

Your printer could indeed do this type of buffering, especially if it is
bi-directional (wouldn't make a good typewriter in the first place anyway
if so).  If it does, you're out of luck.

>     With this in mind then I decided to just try my program with the standard
> input and output devices.  So when I hit a key, I would not only see my 
> keystroke but also another one which my program was then sending.  This too, 
> did not work, a Carriage Return was still needed. 

Interesting.  What should happen is you will see MS-DOS echo characters as you
type them, then when you hit return (after any line-editing) they will all
be handed to your program which would echo them again.  Weird.

So, here is the promised routine which invokes MS-DOS to get a
character from the keyboard (not stdin) without echoing it.  It also
does some things which are obvious from looking at the code.

#include <dos.h>
int
grokchar() {
    union REGS regs;

    regs.h.ah = 7;      /* get char no echo */
    intdos (&regs, &regs);
    return (regs.h.al == 26 ? EOF : regs.h.al == '\r' ? '\n' : regs.h.al);
}

The MS-DOS interrupts are documented in the MS-DOS Technical Reference
Manual, so if you want one that echos you can look it up there.  I think there
is also one to pass a character to the printer.

Stuff like this is why I think MS-DOS stands for Massively Suckful DOS.
Personally, I'd use either a real typewriter or an editor for this job.
-- 
Tom May.	uw-beaver!uw-nsr!uw-warp!tom
(So, do I pass the Turing test?)

backman@interlan.UUCP (Larry Backman) (01/12/87)

In article <1867@sunybcs.UUCP> ugwayne@sunybcs.UUCP (Wayne Nelligan) writes:
>
>
>Subject: Unbuffered I/O using MicroSoft C Version 3.0
>-----------------------------------------------------
>
>
>    A friend of mine recently asked me "If I knew of a program that would let
>your printer act like a typewriter?".  
>    I am using MicroSoft C 3.0 and I have tried various methods but each time
>the printer only prints out when I enter a Carriage Return.  At first, I was
>using getc() and putc() and then it dawned on me that these functions
>use buffered I/O.  
>    So what I would like to know then, Is how would I get a program like
>this to work?  How do you get unbuffered I/O using MicroSoft C 3.0?


	I've never tried this particular problem, but having gone through
	similar experiences with DOS and various C compilers, I would
	suggest the brute forc approach.  Replace the BIOS keyboard and
	printr handlers with your own interrupt handlers, then play
	read a char... write a char... out of the interrupt service
	routines.  Its not as clean as using a C call, but you won't have
	unknowns in your way.

					Larry Backman
					Micom - Interlan, Inc.

					ulowell !interlan!backman

reintom@rocky2.UUCP (Tom Reingold) (01/12/87)

In article <1867@sunybcs.UUCP>, ugwayne@sunybcs (Wayne Nelligan) writes:
> 
>     A friend of mine recently asked me "If I knew of a program that would let
> your printer act like a typewriter?".  Since a program of this sort seemed 
> really easy to write (just get characters and send them to the printer), I
> decided I would just write one myself.  Well, I am sorry to say, things have
> not been as easy as I thought they would be.  The problem is that I can't get
> the printer to just print one character at a time.  It only prints out a line
> at a time.
> [...]
>     So what I would like to know then, Is how would I get a program like
> this to work?  How do you get unbuffered I/O using MicroSoft C 3.0?
> What am I doing wrong?  If anyone has any suggestions, I
> would sure appreciate a response.  This is really driving me crazy.   
> 
>                              Thanks in advance,
>                                    Wayne

You are probably automatically generating unbuffered output but the printer
buffers its input until a line is full.  A line is full either when it gets
a CR or when its length is reached.  You can fool it with the method
outlined in the enclosed program.

Good luck.

Tom
========================================================================
#include <stdio.h>

#define   CR      '\r'
#define   LF      '\n'
#define   TAB     '\t'
#define   SPACE   ' '

/* 
 * This is an example program.  It reads the standard input
 * and prints it to the standard printer, one character
 * at a time.  Tabs and other subtle things will not work 
 * because it is an example to show you that you can do
 * what you want to.
 *
 * The printer prints when the line length is reached or
 * when it gets a RETURN character.  This program sends one
 * after each character.
 *
 */

main()
{
    char line[256];
    int i, j;

    while ((gets(line)) != NULL) {
        for (i=0; i < strlen(line); i++) {
            fputc(line[i], stdprn);
            fputc(CR, stdprn);
            fputc(SPACE, stdprn);
            for (j=0; j < i; j++)
                fputc(SPACE, stdprn);
        }
        fputc(LF, stdprn);
    }
}
-- 
Tom Reingold;  The Rockefeller University; 1230 York Av; NY 10021
PHONE: (212) 570-7709 [office]; (212) 304-2504 [home]
ARPANET: reintom@rockefeller.arpa BITNET: REINTOM@ROCKVAX
UUCP: {seismo|ihnp4|yale|harvard|philabs|phri}!cmcl2!rna!rocky2!reintom

reintom@rocky2.UUCP (Tom Reingold) (01/12/87)

[]

Ok, this program more closely addresses the problem you describe.
As you can see, this small problem has been fun for me.

=================================================================
#include <stdio.h>

#define   CR      '\r'
#define   LF      '\n'
#define   SPACE   ' '
#define   ESC     '\033'

/* 
 * This is an example program.  It directly reads the keyboard
 * and prints to the standard printer, one character
 * at a time.  Tabs and other subtle things will not work 
 * because it is an example to show you that you can do
 * what you want to.
 *
 * The printer prints when the line length is reached or
 * when it gets a RETURN character.  This program sends one
 * after each character.
 *
 * This program exits after the user presses ESCAPE.
 *
 */

main()
{
    int i, linelen, c;

    linelen = 0;
    while ((c=getche()) != ESC) {
        fputc(c, stdprn);
        if (c != CR) {
            linelen++;
            fputc(CR, stdprn);
            for (i = 0; i < linelen; i++)
                fputc(SPACE, stdprn);
            fflush(stdprn);
        }
        else {
            linelen = 0;
            putchar(LF);
            fputc(LF, stdprn);
            fflush(stdprn);
        }
    }
    fputc(LF, stdprn);
}
-- 
Tom Reingold;  The Rockefeller University; 1230 York Av; NY 10021
PHONE: (212) 570-7709 [office]; (212) 304-2504 [home]
ARPANET: reintom@rockefeller.arpa BITNET: REINTOM@ROCKVAX
UUCP: {seismo|ihnp4|yale|harvard|philabs|phri}!cmcl2!rna!rocky2!reintom

campbell@maynard.BSW.COM (Larry Campbell) (01/14/87)

Summary: Can't read a character at a time using Microsoft C;  program
	 sees no input until carriage return typed.

	 A respondent erroneously blamed the printer on which the C program
	 was echoing its output;  the respondent failed to note that the
	 original poster had thought of that and had tried sending output
	 to the console, with identical results.

This problem has nothing to do with Microsoft C.  It is a feature of DOS.
Standard input (the console) is normally opened by DOS in ASCII mode, in
which a program reading from stdin blocks until a carriage return is typed.

To be woken up on every character, you need to put stdin into binary mode.
You do this with the IOCTL DOS call (INT 21, AH=44).  Do subfunction AL=0
first to get the mode bits (returned in DX), then set the "binary" bit
(bit 5), then do subfunction 1 to set the mode bits (from DX).  For both
calls, the file handle goes in BX.

Note that binary mode will also disable ^S and ^C processing.  Don't forget
to put the console back in ASCII mode!
-- 
Larry Campbell                                The Boston Software Works, Inc.
Internet: campbell@maynard.uucp             120 Fulton Street, Boston MA 02109
uucp: {alliant,wjh12}!maynard!campbell              +1 617 367 6846
ARPA: campbell%maynard.uucp@harvisr.harvard.edu      MCI: LCAMPBELL

sbanner1@uvicctr.UUCP (S. John Banner) (01/15/87)

In article <645@uw-warp.UUCP> tom@uw-warp.UUCP (Tom May) writes:
>In article <1867@sunybcs.UUCP>, ugwayne@sunybcs (Wayne Nelligan) writes:

>So, here is the promised routine which invokes MS-DOS to get a
>character from the keyboard (not stdin) without echoing it.  It also
>does some things which are obvious from looking at the code.
>
>#include <dos.h>
>int
>grokchar() {
>    union REGS regs;
>
>    regs.h.ah = 7;      /* get char no echo */
>    intdos (&regs, &regs);
>    return (regs.h.al == 26 ? EOF : regs.h.al == '\r' ? '\n' : regs.h.al);
>}

you can actually do the same thing without resorting to DOS in mscv4.0,
and I understand it works in earlyer versions as well (I got the trick
from a freind of mine using v2.something).

grokchar()
{
while (!kbhit())
   ;
return getc();
}

I am not totally sure that I have the right funtion names here, but I
know they are pretty close (you might try fgetc if getc doesn't work),
and I may have the wrong name for kbhit, but the names should be pretty
obvious.

                      S. John Banner

...!uw-beaver!uvicctr!sbanner1
ccsjb@uvvm
sbanner1@uvunix.UVIC.CDN

tom@uw-warp.UUCP (01/25/87)

In article <645@uw-warp.UUCP>, I, tom@uw-warp.UUCP (Tom May), wrote:
> So, here is the promised routine which invokes MS-DOS to get a
> character from the keyboard (not stdin) without echoing it.  It also
> does some things which are obvious from looking at the code.
> 
> #include <dos.h>
> int
> grokchar() {
>     union REGS regs;
> 
>     regs.h.ah = 7;      /* get char no echo */
>     intdos (&regs, &regs);
>     return (regs.h.al == 26 ? EOF : regs.h.al == '\r' ? '\n' : regs.h.al);
> }

It turns out that the statement about reading the keyboard, not stdin, was
incorrect.  Although the function is called Direct Console Input or something
like that (my DOS machine + manuals are 15 miles away from my USENET machine),
further inspection of the DOS Technical Reference manual reveals that all those
so-called console routines actually read stdin.  Even the Keyboard Input
function reads stdin!  (At least the doc says it does, I haven't tried it.)
I discovered this when I redirected the input to a program I had written that
used the above function and found that it worked, much to my surprise.

Also, the MSC library routines getch() and getche() use DOS functions which
read characters from stdin, not con as the manual seems to imply.

I just wanted to clear this up before someone else made me look bad. :-)









-- 
Tom May.	uw-beaver!uw-nsr!uw-warp!tom

perry@omepd.UUCP (01/28/87)

In article <650@uw-warp.UUCP> tom@uw-warp.UUCP (Tom May) writes:
> [Describing a way to call the MS-DOS INT 21 direct console input function]
> ...
>It turns out that the statement about reading the keyboard, not stdin, was
>incorrect.  Although the function is called Direct Console Input or something
>like that (my DOS machine + manuals are 15 miles away from my USENET machine),
>further inspection of the DOS Technical Reference manual reveals that all those
>so-called console routines actually read stdin.  Even the Keyboard Input
>function reads stdin!  (At least the doc says it does, I haven't tried it.)
>
>Also, the MSC library routines getch() and getche() use DOS functions which
>read characters from stdin, not con as the manual seems to imply.

Right! This one got me too, first time I tried it. My MS-DOS programmer's
reference says *standard input* all the way, but with a name like *console
input*...

There is a quite simple way to make sure that stdin is really the keyboard.
The (MSC) call sequence
	close(0);
	dup(2);
effectively duplicates the stderr channel onto stdin. As you can't redirect
stderr in MSDOS (at least from COMMAND.COM), this is sure to be the keyboard.
That sequence loses whatever was redirected into stdin, of course. If you
need it, you can use code like this:
	FILE *stin;
	stin=fopen("NUL","r");		/* grab a free file slot */
	close(fileno(stin)); dup(0);	/* duplicate stdin into stin */
	close(0); dup(2);		/* duplicate stderr into stdin */
From then on, *stdin* is the keyboard and *stin* is what was piped into your
program.
It seems that the theoretically different I/O modes (stderr writing, stdin
reading) don't make any difference to MS-DOS. Btw, this code should work
in any language that accesses MS-DOS file handles directly; it uses a MS-DOS
feature, not a MSC speciality.

Hope this help you...
------------------------------------------------------------------------
  <<  Perry The Cynic >>	      ...!tektronix!ogcvax!omepd!inteloa!perry
						...!verdix!omepd!inteloa!perry
    (Peter Kiehtreiber)			      -or try- perry@inteloa.intel.com

bet@ecsvax.UUCP (01/29/87)

While setbuf will still be needed to get unbuffered I/O, there is a
wondrously simple way to ensure that I/O doesn't get redirected without
mucking about with DOS interrupts! Like so:

	FILE *fopen(), *console;

	console = fopen("\\dev\\con", "r");

The '\' characters are doubled because '\' is the quoting character in C
strings; you can simplify the above to the more obvious:

	FILE *fopen(), *console;

	console = fopen("con", "r");

(or "con:", or whatever floats your boat) if you are guaranteed that
your program will never be run on a system using the undocumented DOS
AVAILDEV function to hide devices.

-Bennett

P.S. AVAILDEV:

Issue DOS function call INT 21H with AH=37H to access SWITCHAR and
AVAILDEV:

	AL=0	read SWITCHAR value into DL
	AL=1	set SWITCHAR from DL
	AL=2	read AVAILDEV into DL
	AL=3	set AVAILDEV from DL

The SWITCHAR is the character used for "switches" in DOS command
arguments (defaults to '/', as in "DIR /P"). '-' is popular to make a
system look more like UNIX; if the SWITCHAR is anything other than '/',
then '/' may be used instead of '\' for pathnames (even more like
UNIX!). AVAILDEV=0 means than devices must be referenced in an imaginary
subdirectory "\dev" (shades of UNIX's /dev/*); a filename "prn.dat" can
be created on disk and manipulated like any other. If AVAILDEV != 0 then
device names are recognized anywhere (this is the default): "prn.dat"
is synonymous with "prn:".

NOTE: These functions are undocumented, whicn means they are
unsupported, which means they are liable to go away whenever Microsoft
is feeling frisky. Use at your own risk! However, PLEASE program around
them to make life nicer for the daring souls who take the risk.

-Bennett
-- 
Bennett Todd -- Duke Computation Center, Durham, NC 27706-7756; (919) 684-3695
UUCP: ...{decvax,seismo,philabs,ihnp4,akgua}!mcnc!ecsvax!duccpc!bet
BITNET: DBTODD@TUCC.BITNET -or- DBTODD@TUCCVM.BITNET -or- bet@ECSVAX.BITNET
terrorist, cryptography, DES, drugs, cipher, secret, decode, NSA, CIA, NRO.

kneller@ucsfcgl.UUCP (02/01/87)

In article <2609@ecsvax.UUCP> bet@ecsvax.UUCP (Bennett E. Todd III) writes:
>
>While setbuf will still be needed to get unbuffered I/O, there is a
>wondrously simple way to ensure that I/O doesn't get redirected without
>mucking about with DOS interrupts! Like so:
>
>	FILE *fopen(), *console;
>
>	console = fopen("\\dev\\con", "r");

One caveat is that the open will sometimes fail running under DOS 2.x.
DOS 3.x doesn't have this problem.
	Don Kneller
UUCP:	...ucbvax!ucsfcgl!kneller
ARPA:	kneller@cgl.ucsf.edu
BITNET:	kneller@ucsfcgl.BITNET