[comp.lang.c] Sticky IBM C programming problems

alex@bilver.UUCP (Alex Matulich) (02/24/90)

Thanks everybody!
I got inundated with responses to my C programming questions, and here
is the summary, as concise as I could make it.  My original post is quoted
here with the ">" symbol, and my comments to the answers are indicated.

>For the past couple of years I have been writing C programs for IBM
>compatibles using either Microsoft C or Turbo C.  I try to make my software
>look and feel as professional as possible, but during my endeavors I have
>run into some programming problems that I can't figure out, and none of
>the IBM-specific C programming books I have read are any help.  Commercial
>programs do these things, so why can't I?

COMMENT:  Most of the responses I got to my questions were very informative
 and amounted to a polite way of saying "RTFM".  I realized what my problem
 was:  Both the MSC 5.1 and Turbo C 2.0 compilers I use belong to the
 university I work for, and the MSC manuals do NOT answer my questions, which
 is why I asked them; the Turbo C manuals do, however the ones I had
 were from version 1.5 or earlier and I didn't know it!

 I have been using primarily MSC.  Almost every single respondent is a
 Turbo C user, so many of the responses didn't apply to MSC -- I didn't
 realize Turbo C was so popular.  Anybody know anything about Lattice?


>1) Is there a faster way to display character strings than using puts()
>   or printf() without resorting to assembly language?

Use cputs() and cprintf() in the <conio.h> header file.
Use putch(), it's "direct video", might be faster than putc()/putchar()
Use _write().  It is a direct interface to the DOS write call, and does none
of the translations etc. that write() or puts() do.

COMMENT:  I was hoping to preserve portability in my programs by using
 cprintf() everywhere, and, in porting it to a non-IBM computer, using
 #define cprintf printf, but cprintf() doesn't translate the \n character
 properly, nor does it work with ANSI escape codes.  But I do have uses.
 cprintf() in Microsoft C is just as SLOW as printf()!  Turbo C is much
 faster, but the text doesn't inherit the current stdout text colors.
 As far as I can tell, _write() needs a file handle, and I don't know how
 to get a file handle to the video memory.


>2) If I have a text screen set up like I want it, is it possible to save
>   it away somehow so I can display it anytime later by dumping it back
>   into the video memory?  If so, how do I find the address and length 
>   of a video text display?

Curses might do this for you.
Use gettext(), movetext(), puttext() in <conio.h>
For graphics, use _getimage() and _putimage().
On monochrome video cards, (mode 7) the video memory starts at location
0xB000:0000.  In all other text modes, video memory starts at 0xB800:0000.
Use _getvideomode() to determine whether you're mono or color.
 The video memory is arranged in row-major order with two bytes per
 character.  Thus, the address of any character on the screen is
 (base) + (row * 160) + (column * 2).  The first byte is the ASCII code for
 the character, and the second byte is the character's display attribute.

COMMENT:  gettext(), etc. is only available on Turbo C.
 My current project is too big to convert to curses, but I'll look for
 it next time.


>3) How do I display inverse, boldface, or multicolored text without
>   resorting to using ANSI.SYS?

Use textbackground(), textcolor()
Use textattr() with cputs() in <conio.h>
Use escape sequences with puts()
(The programs will be portable to non-completely compatible machines if you
use the escape sequences)

COMMENT:  textbackground() and textcolor() aren't available with MSC.
 Escape sequences don't work with cputs(), cprintf(), etc. but it's not
 a real problem to use escape sequences with stdout functions when speed
 doesn't matter.


>4) The critical error handler gets invoked when the printer needs more
>   paper.  How do I tell if the printer is offline?  The computer hung up
>   last time I tried using fopen() to get a file handle to a printer that
>   was powered off.

Replace the critical error handler (using the setvect() function) with your
 own.  It should check everything you're interested in, and the rest should
 be ignored or passed to the original handler.  Be sure to replace the
 original when you're done!
Use biosprint() in <bios.h>               (Turbo C)
Use _bios_printer(_PRINTER_STATUS, ...);  (MSC)
Some machines have very long timeouts to decide whether or not the printer
 is offline.  You could trap the critical error and check the error code
 yourself with harderr()/hardretn().  If you need to talk directly to a
 printer, use stdprn which should be already opened.
Write to a file and then pass it to the print spooler and you get background
 printing and no worries about programs hanging.

COMMENT:  Hmmm, that's right -- if I wait LONNNG enough, the critical error
 handler does indeed get invoked with a "device not ready" message.  From
 the responses I got, I gather that there's not really any good way to do
 this.  And how do I access "the print spooler"??  Is it a part of MS-DOS?


>5) If a user of my software has a serial printer, how do I set up the
>   serial port parameters (baud rate, xon/xoff, databits, stopbits, etc)?

Use _bios_serialcom()   (MSC)
Use bioscom()           (Turbo C)
Let the user take care of this when he installs the printer.  It is
 not likely to change very often; applications should not need to
 worry about such things.

COMMENT:  If there's a printer on the serial port, it seems virtually
 impossible to tell its status without first knowing what kind of printer
 it is.


>6) I expected to find a function somewhere in the standard library that
>   would read the contents of a disk directory into a string array.  Did
>   I miss it?  I have heard the Lattice compiler has such a function, but
>   I don't have that compiler.  If no such function exists, how is it done?

You use findfirst() and findnext() to step through the directory one matching
entry at a time (this is Turbo C, MSC calls them _dos_findfirst() and
_dos_findnext()).  Also see getcurdir(), getcwd().

COMMENT:  Sure enough, there it is in the MSC manual!  The MSC index listed
 nothing useful under "directory", "directory control", "files", and other
 obvious headings.  _dos_findfirst(), indeed!  I never looked at that one
 long enough to realize what it was.  Also, implementation differences
 between these functions in Turbo C and MSC make for difficult porting.


>7) If my PATH environment variable is set up so that I can run one of my
>   programs from a directory different than the one containing my program,
>   how it possible for my program to determine the directory IT started
>   from?  Or does that information have to be hard-coded in my program?

Look in argv[0], it contains the path of the program.  Then use fnsplit()
 and fnmerge() to manipulate the path.  Works under DOS 3.x.
Use searchpath().
In version 1.x and 2.x, your best bet is to search the current directory,
 and then the path, yourself, which is a pain.

COMMENT:  By golly, it works!  I still don't see it in the MSC manual.  The
only time argv[0] is mentioned is in connection with spawned tasks.


>If you know the answers to at least one of the above questions, or if you
>know the title of a helpful book I can look up, PLEASE e-mail me a reply.

Norton's (Microsoft Press) "Inside the PC"
The manuals that came with Turbo C
For programming the serial port see The Waite Groupe's
       MS-DOS Developer's Guide.
Hunt, "The C Toolbook"
Sam's - Waite Group - Microsoft C for the IBM PC
Ray Duncan's Advanced MSDOS (2nd ed.)
Peter Norton's Programmer's Guide to the IBM PC


Many thanks to all the people who contributed (did I leave anyone out?):

uunet!copper.wr.tek.com!michaelk (Michael D. Kersenbrock)
uunet!bosco.Berkeley.EDU!raymond (Raymond Chen)
RAMontante <uunet!iuvax!bobmon>
Dan Kahn <uunet!rufus.math.nwu.edu!kahn>
uunet!mitisft!dold
uunet!jarthur!dfoster (Derek Foster)
uunet!demott.COM!kdq (Kevin D. Quitt)
uunet!b.gp.cs.cmu.edu!Ralf.Brown
uunet!cis.ohio-state.edu!calvin!richard (Richard Brittain)
uunet!hubcap.clemson.edu!wkay (W. Kevin Kay)
Rajiv Partha Sarathy <uunet!gpu.utcs.utoronto.ca!sarathy>
"Rich Walters" <uunet!math.arizona.edu!arizona!raw>
Russell Herman <uunet!me.utoronto.ca!rwh>
uunet!att!pegasus!psrc (Paul S. R. Chisholm)
uunet!charyb!will (Will Crowder)
Tom Wilson  <uunet!uhccux.uhcc.Hawaii.Edu!wilson>
ucf-cs!cdis-1!tanner
CMH117@psuvm.psu.edu (Charles Hannum)

-- 
     ///  Alex Matulich
    ///  Unicorn Research Corp, 4621 N Landmark Dr, Orlando, FL 32817
\\\///  alex@bilver.UUCP    ...uunet!tarpit!bilver!alex
 \XX/  From BitNet use: bilver!alex@uunet.uu.net

CMH117@psuvm.psu.edu (Charles Hannum) (02/25/90)

I seem to have missed your original posting, so I'll take this opportunity to
respond.


In article <498@bilver.UUCP>, alex@bilver.UUCP (Alex Matulich) says:
>
>                      ...   the MSC manuals do NOT answer my questions, which
> is why I asked them; the Turbo C manuals do, however the ones I had
> were from version 1.5 or earlier and I didn't know it!
>
> I have been using primarily MSC.  Almost every single respondent is a
> Turbo C user, so many of the responses didn't apply to MSC -- I didn't
> realize Turbo C was so popular.  Anybody know anything about Lattice?

Probably because Turbo C is *fast*, the integrated environment in one *hell*
of a lot better than M*crosoft's, and the manuals are superb!


>COMMENT:  I was hoping to preserve portability in my programs by using
> cprintf() everywhere, and, in porting it to a non-IBM computer, using
> #define cprintf printf, but cprintf() doesn't translate the \n character
> properly, nor does it work with ANSI escape codes.  But I do have uses.
> cprintf() in Microsoft C is just as SLOW as printf()!  Turbo C is much
> faster, but the text doesn't inherit the current stdout text colors.
> As far as I can tell, _write() needs a file handle, and I don't know how
> to get a file handle to the video memory.

Lemme explain ...

cprintf(), et al., are specified to write directly to the CON: device (or
/dev/ttyxx, or whatever), rather than to stdout or stderr.  They are
*supposed* to do the same translations as printf() (et al.); I suspect this
is why the MSC library routines seem to offer no improvement in speed over
the normal printf() routines; this is not what they were meant for.

In Turbo C 1.0 (I've had it since the beginning!) the output was sent directly
to the MS-DOS console I/O functions (rather than the file I/O functions, which
are normally used).  However, this didn't follow the definition either, as they
could be redirected.

Starting with Turbo C 1.5, the console library has forced it to write directly
to the screen (literally!!!  B-), but it still doesn't do proper translations;
you still have to use "\n\r" rather than "\n".  <sigh>

If you just want to speed up output, and you're displaying line-by-line, use
setvbuf(stdout,... _IOLBF ...) (not exactly; fill in the missing arguments!).
I don't know about MSC, but this DRAMATICALLY improves performance in Turbo C.
(The reason, for those interested, is that Turbo C says "Hey!  It's a character
device!" and proceeds to write ONE STINKING CHARACTER at a time.  With MS-LOSS
this is horrendously slow.  By using _IOLBF, a whole line is output on each
MS-LOSS call.  [Note:  _IOFBF on an output file is the same in TC as _IOLBF.])


>COMMENT:  gettext(), etc. is only available on Turbo C.
> My current project is too big to convert to curses, but I'll look for
> it next time.

I have yet to see a decent (read: REASONABLY FAST) version of Curses! on the
PC.  If someone actually has a copy, I'd appreciate it, as I don't generally
make a habit of porting brain-dead software.


>COMMENT:  textbackground() and textcolor() aren't available with MSC.
> Escape sequences don't work with cputs(), cprintf(), etc. but it's not
> a real problem to use escape sequences with stdout functions when speed
> doesn't matter.

In your portability quest, you are not considering the problem of porting to
an entirely different *architecture*.  What if you're using a Tektronix
terminal on a *nix machine?  The escape sequences will be completely different!
Your best option, if you want total portability, is to use a *nix-like
"termcap" file.


>COMMENT:  Hmmm, that's right -- if I wait LONNNG enough, the critical error
> handler does indeed get invoked with a "device not ready" message.  From
> the responses I got, I gather that there's not really any good way to do
> this.  And how do I access "the print spooler"??  Is it a part of MS-DOS?

The "print spooler" is PRINT.COM.  It's documented in the MS-DOS manual.


>COMMENT:  Sure enough, there it is in the MSC manual!  The MSC index listed
> nothing useful under "directory", "directory control", "files", and other
> obvious headings.  _dos_findfirst(), indeed!  I never looked at that one
> long enough to realize what it was.  Also, implementation differences
> between these functions in Turbo C and MSC make for difficult porting.

These functions are not derived for any type of standard, and therefore cannot
be expected to be portable.  Tough.


>Look in argv[0], it contains the path of the program.  Then use fnsplit()
> and fnmerge() to manipulate the path.  Works under DOS 3.x.
>Use searchpath().
Note:  This was in reference to the statement below, which indicates that in
       DOS -3.00, argv[0] doesn't contain the complete path.
>In version 1.x and 2.x, your best bet is to search the current directory,
> and then the path, yourself, which is a pain.
Note:  Version 1.x doesn't use paths.  [B-)]


>[What books do you recommend?]
>The manuals that came with Turbo C
You'll get no argument on that one!  They're excellent!

(Herbert Schildt's?) "Turbo C Pocket Reference".  I'm not sure it's ever been
updated since version 1.5, but it's still an incredibly useful book.


>Many thanks to all the people who contributed (did I leave anyone out?):
>
> ...
>CMH117@psuvm.psu.edu (Charles Hannum)

I'm at the bottom of the list!  <wimper>  B-)


Virtually,
- Charles Martin Hannum II       "Klein bottle for sale ... inquire within."
    (That's Charles to you!)     "To life immortal!"
  cmh117@psuvm.{bitnet,psu.edu}  "No noozzzz izzz netzzzsnoozzzzz..."
  c9h@psuecl.{bitnet,psu.edu}    "Mem'ry, all alone in the moonlight ..."

tlmfe@EE.ECN.PURDUE.EDU (Mark B Strong) (03/02/90)

Quick question:  Why won't an environmental variable I change within
a C program stay after the program is terminated?  Setenv() doesn't seem
to work?  

Thanks,


-- 
+-------------------------------------------------------------------------+
| Mark Strong            |  tlmfe@ei.ecn.purdue.edu                       | 
| Purdue University      |                                                | 
| Those who know what's best for us must rise and save us from ourselves. |
+-------------------------------------------------------------------------+

raw@math.arizona.edu (Rich Walters) (03/02/90)

In article <9003012109.AA07700@ei.ecn.purdue.edu> tlmfe@EE.ECN.PURDUE.EDU (Mark B Strong) writes:
>
>Quick question:  Why won't an environmental variable I change within
>a C program stay after the program is terminated?  Setenv() doesn't seem
>to work?  
>


Assuming this is in a UNIX machine:

The program is run in a separate shell unless you explicitly run it in the
current shell (not recommened.)  Thus the program only 'sees' the envirion-
ment variables that are explicitly EXPORTED from your login shell.

I am fairly sure that this is correct for the bourne shell and the korn shell.
I don't know about the c shell.

				Richard Walter

-------------------------------------------------------------------------------

			Keep on crunching those numbers

-------------------------------------------------------------------------------

cpcahil@virtech.uucp (Conor P. Cahill) (03/02/90)

In article <9003012109.AA07700@ei.ecn.purdue.edu> tlmfe@EE.ECN.PURDUE.EDU (Mark B Strong) writes:
>
>Quick question:  Why won't an environmental variable I change within
>a C program stay after the program is terminated?  Setenv() doesn't seem
>to work?  

First of all, this isn't a C questions it is an OS question so I have forwarded
followups to comp.sys.ibm.pc

The answer is that a programs environmental variables are stored in that
program's data space is are lost when the program exits.  The only thing
special about environment variables is that there exists a convention for
passing that portion of your data space to all child processes, but you cannot
use it to change the data space of a parent process (which is what you are
apparently trying to do).

Of course, your program could pass the environment variable changes back
through a file descriptor (say stdout) and the parent program could 
make these changes in it's environment.  But you will have to modify both
programs to do this.

-- 
+-----------------------------------------------------------------------+
| Conor P. Cahill     uunet!virtech!cpcahil      	703-430-9247	!
| Virtual Technologies Inc.,    P. O. Box 876,   Sterling, VA 22170     |
+-----------------------------------------------------------------------+

henry@utzoo.uucp (Henry Spencer) (03/03/90)

In article <9003012109.AA07700@ei.ecn.purdue.edu> tlmfe@EE.ECN.PURDUE.EDU (Mark B Strong) writes:
>Quick question:  Why won't an environmental variable I change within
>a C program stay after the program is terminated?  Setenv() doesn't seem
>to work?  

Assuming you are running on a Unix system...  The environment variables
your program gets are *copies* of those in its parent shell, and are in
the program's own address space.  There is no way for the program to
affect its parent's variables.
-- 
MSDOS, abbrev:  Maybe SomeDay |     Henry Spencer at U of Toronto Zoology
an Operating System.          | uunet!attcan!utzoo!henry henry@zoo.toronto.edu