home *** CD-ROM | disk | FTP | other *** search
-
- ΓòÉΓòÉΓòÉ 1. Why this file? ΓòÉΓòÉΓòÉ
-
- I've been converting hundreds of kilobytes of old C code, running under PC DOS
- and OS/2 1.x and compiled with Microsoft C 5.x and 6.x, to run with IBM C Set/2
- under OS/2 2.x. As I solved the problems that I encountered, I took these notes
- to help me remember what I did if I encounter the same problem again. I then
- converted this file into IPF as an exercise in the latter and am distributing
- it, hoping that it will be useful to others. These notes are meant to
- supplement, not replace, the C Set/2 User's Guide and Migration Guide, although
- I found them pretty useless. They are not, nor were ever meant to be,
- well-organized.
-
- I would greatly appreciate all comments, suggestions, job offers :), et al.
- E-mail me at DLV@DM.COM, or call me at +1-718-261-6839.
-
-
- ΓòÉΓòÉΓòÉ 2. Configuring the compiler ΓòÉΓòÉΓòÉ
-
- The following lines in my CONFIG.SYS configure the compiler:
-
- libpath=...;C:\IBMWF\DLL;C:\TOOLKT20\DLL;C:\IBMC\DLL;...
- set path=...;C:\IBMWF\BIN;C:\TOOLKT20\OS2BIN;C:\IBMC\BIN;...
- set dpath=...;C:\IBMC\LOCALE;C:\IBMC\HELP
- set icc=-Ka+ -Kb+ -Kc+ -Ko+ -Kp+ -Kx+ -N10 -W3 -Ss+ -G4 -Gf+ -Gs+ -O+ -Q+
- -DINCL_32 -Gd+
- set tempmem=on
- set lib=C:\IBMLAN\NETSRC\LIB;C:\TOOLKT20\OS2LIB;C:\IBMC\LIB
- set include=C:\IBMLAN\NETSRC\H;C:\TOOLKT20\C\OS2H;C:\IBMC\INCLUDE
- set tmp=C:\TEMP\
-
- Explanation of the ICC options:
-
- -K* enables some useful warnings
- -N10 stops after 10 errors
- -Ss+ allows //, the C++ extension for comment
- -G4 optimizes for 486, rather than 386
- -Gf+ faster, non-IEEE, floating point
- -Gs+ no stack probes
- -O+ optimize
- -Q+ logo
- -DINCL_32 used in various .h files (LAN Server)
- -Gd+ DLL runtime for smaller EXE
-
- Note: if you use -gd+, then at runtime DPATH must include
- C:\IBMC\LOCALE;C:\IBMC\HELP. If you intend to run the EXE file on machines
- where ICC is not installed, you must specify -gd-.
-
- You may also want to specify -Sm to enable the migration features (see the
- section on those).
-
-
- ΓòÉΓòÉΓòÉ 3. At the beginning ΓòÉΓòÉΓòÉ
-
- Look through the #include files and get rid of the ones you don't need. In
- particular, <dos.h> does not exist and <fcntl.h> and <io.h> should be
- eliminated.
-
- Whenever you rely on an integer variable being 16, rather than 32 bits, you
- must declare it to be short. In my practice this happens usually when
- describing out structures used for binary i/o on files. For example,
-
- static _Packed struct {
- char header[7];
- int num_row;
- char quote;
- char line[8];
- char trailer;
- } rec;
-
- The program will compile, but when an attempt it made to read this structure
- from a file, the quote field will receive the wrong byte. This is because
- num_row should have been explicitly declared short. Always declare such fields
- long or short.
-
- Also note that if _Packed were not specified, the compiler would insert a
- filler byte before num_row to align it on 16-bit boundary and leading to
- incorrect results. long double (80-bit temporary real) are aligned on 16-byte
- boundary, with 4 bytes wasted. This differs from some C compilers that align
- them on 10-byte boundary.
-
- Changed handling of char type may lead to bugs. In many old compilers, the
- default for char is signed. In icc, the default is unsigned. This makes sense
- if the chars contain 8-bit text (e.g., French or German). If the char variables
- really contain numbers, why not use int.
-
- Old code:
-
- char c;
-
- c=-1;
- produces a warning and c becomes 255.
-
- New code:
-
- signed char c;
- You can specify -J+ to make default char signed.
-
-
- ΓòÉΓòÉΓòÉ 4. Migrating from old compilers ΓòÉΓòÉΓòÉ
-
- These notes refer to migrating from really old compilers, such as MS C 5.x.
-
- Provide new-style prototypes for every function.
-
- Old code:
-
- myfunc(a,b)
- int a;
- int b;
- {
- ...
- }
-
- New code:
-
- static int myfunc(int a,int b);
- ...
- int myfunc(int a,int b)
- {
- ...
- }
-
- Do not omit the ... when specifying variable arguments.
-
- Old code:
-
- #include <stdarg.h>
-
- void wrlog(char *s,);
-
- New code:
-
- static void wrlog(char *s,...);
- ...
- void wrlog(char *s,...)
- {
- va_list arg_ptr;
-
- va_start(arg_ptr, s);
- vprintf(s,arg_ptr);
- fflush(stdout);
- vfprintf(logfptr,s,arg_ptr);
- fflush(logfptr);
- va_end(arg_ptr);
- }
- Beware of trigraphs! E.g., dbfcmd(..."update #t set name='???'\n"); will
- actually put a ^ (caret) in name, leading to an insidious bug. Fortunately, icc
- produces a warning, unlike MS C 6. The solution is to avoid using multiple ?'s.
-
- You cannot omit type and just declare a variable name:
-
- int myfunc(void)
- {
- a;
-
- a=123...
- }
- In icc, say int a.
-
- NULL is a pointer to void and cannot be compared to 0 without a cast.
-
-
- ΓòÉΓòÉΓòÉ 5. Differences in library functions ΓòÉΓòÉΓòÉ
-
- Replace non-standard functions by their standard equivalents:
- unlink("junkfile"); by remove("junkfile");, onexit by atexit...
-
- MS C closes all streams when the program terminates, and ICC doesn't. If you
- want this behavior, add a _fclosall() to your atexit handler.
-
- Under MS C, the atexit handler is not invoked if the program abends. Under icc
- it's always invoked.
-
- Old code:
-
- int exit_rtn(void);
- ...
- if (0!=atexit(exit_rtn))
- error("add exit_rtn() to exit list", 1);
-
- New code:
-
- static void exit_rtn(void);
- volatile static int needonexit=1;
- ...
- void exit_rtn(void)
- {
- {
- if (needonexit)
- {
- needonexit=0;
- /* logout, hangup, etc. */
- _fcloseall();
- }
- }
-
- Note that the routine returns void instead of int.
-
- Add fflush(stdout); before each scanf.
-
- printf("Enter a number: ");
- fflush(stdout);
- scanf("%d",&n);
-
- qsort and bsearch are very picky about the comparison function they take as an
- argument.
-
- Old code:
-
- static int compare_cap(const int *a,const int *b);
- static int iwrank[MAX_MASTER];
- ...
- int compare_cap(const int *a,const int *b)
- {
- return (cap[*a]<cap[*b]) ? 1 : ((cap[*a]==cap[*b]) ? 0 : -1);
- }
- ...
- for (i=0; i<num_work; i++)
- iwrank[i]=i;
- qsort(iwrank, num_work, sizeof(int), compare_cap);
-
- New code:
-
- static int compare_cap(const void *a,const void *b);
- ...
- int compare_cap(const void *a,const void *b)
- {
- return (cap[*(int*)a]<cap[*(int*)b]) ? 1 :
- ( (cap[*(int*)a]==cap[*(int*)b]) ? 0 :
- -1);
- }
-
- Here's an example of a program that uses signal to intercept Control-Break.
- Note that the variables shared between the signal handler and the main program
- must be declared volatile.
-
- #include <signal.h>
-
- static void break_handler(int SIG_TYPE); /* prototype */
-
- volatile static int num_breaks=0; /* incremented by the handler */
-
- int main(void)
- {
- /* install the signal handler */
- if (signal(SIGINT,break_handler)==SIG_ERR)
- perror("Couldn't install breaker handler");
- return(1);
- }
- while (num_breaks<10)
- {
- printf("%d",num_breaks); fflush(stdout);
- }
- return(0);
- }
-
- void break_handler(int SIG_TYPE)
- {
- signal(SIG_TYPE,break_handler); /* reestablish the signal handler */
- printf("\nCtrl-Break has been hit!\n");
- num_breaks++;
- }
-
-
- ΓòÉΓòÉΓòÉ 6. Eliminating migration code ΓòÉΓòÉΓòÉ
-
- The rewritings listed here are not really required. You may enable these calls
- by specifying the /Gm option. However read/write seem to be much slower than
- fread/fwrite under ICC and slow down the program significantly when reading a
- large file or when called in a loop.
-
- Eliminate huge, near, far, cdecl, pascal, and similar keywords.
-
- Replace calls to open/read/write/close and other functions in <io.h> by calls
- to fopen/fread/fwrite/fclose. Since you are no longer limited to reading and
- writing 32K at a time, your code tends to become much shorter.
-
- Old code:
-
- char huge *p;
- int handle;
- unsigned long nread, nleft;
-
- if ((handle=open(CUSIPSDIR, O_RDONLY | O_BINARY))==-1)
- error("can't open CUSIP directory",1);
-
- /* have to read in 32K chunks */
- p=(void*)cusips_dir;
- nread=0;
- nleft=(long)sizeof(struct cusips_dir_row)*MAX_CUSIPS_DIR;
- while (nleft!=0){
- if (nleft>32768)
- nbytes=32768;
- else
- nbytes=(int)nleft;
- nbytes=read(handle,p, nbytes);
- if (nbytes==0) break;
- p+=nbytes;
- nleft-=nbytes;
- nread+=nbytes;
- }
- num_cusips_dir=nread/sizeof(struct returns_dir_row);
-
- close(handle);
- or, if the file is known to be under 32K, just:
-
- nread=read(handle,cusips_dir, sizeof(cusips_dir));
-
- while (sizeof(setlist)==read(handle,&setlist,sizeof(setlist)))
- {
- }
- or worse yet, when opening a file for writing,
-
- if ((handle=open(FTEMP, O_CREAT | O_TRUNC | O_BINARY | O_WRONLY,
- S_IREAD | S_IWRITE)) == -1)
- If you omit the third argument, MS C sets the read-only bit in the file's
- directory entry.
-
- New code:
-
- FILE *fptr;
-
- if (NULL==(fptr=fopen(CUSIPSDIR, "rb")))
- error("can't open CUSIP directory",1);
- num_cusips_dir=fread(&cusips_dir,sizeof(cusips_dir[0]),MAX_CUSIPS_DIR,fptr);
- fclose(fptr);
-
- or in a loop
-
- while (1==fread(&setlist,sizeof(setlist),1,fptr))
-
- If the program uses lseek to determine the file size, here is the standard
- code:
-
- fseek(fptr, 0, SEEK_END);
- filesize=ftell(fptr);
- fseek(fptr, 0, SEEK_SET);
-
-
- ΓòÉΓòÉΓòÉ 7. Linking with 16-bit code ΓòÉΓòÉΓòÉ
-
- These examples deal primarily with the SQL server and LAN Server 2.0, because
- these are the only 16-bit libraries I have to deal with.
-
- Whenever a pointer is passed to a 16-bit function, it must be cast to *_Seg16.
- Usually this is done automatically if the callee is prototyped as expecting
- such pointer, but if you use variable arguments, you must cast explicitly.
-
- Old code:
-
- char *cusips;
-
- dbfcmd(dbproc,"select price from prices where cusip='%8.8s'",
- cusips);
-
- New code:
-
- dbfcmd(dbproc,"select price from prices where cusip='%8.8s'",
- (char*_Seg16)cusips);
-
- Likewise, you must specify %ld when passing int.
-
- Whenever a pointer is returned by a 16-bit code (as a return value from a
- function or as a part of a structure), it must be cast as (type *). Examples:
-
- memcpy(this_line.cusip,(char*)dbdata(dbproc,1),8);
- ...
- NetWkstaGetInfo(NULL,10,(char *)wksta_info_out,sizeof(wksta_info_out),&n);
- wrlog("Computername=%s Domain=%s Username=%s\n",
- (char*)wksta_info_out[0].wki10_computername,
- (char*)wksta_info_out[0].wki10_langroup,
- (char*)wksta_info_out[0].wki10_username);
-
-
- ΓòÉΓòÉΓòÉ 8. Linker ΓòÉΓòÉΓòÉ
-
- Here is a sample .DEF file:
-
- NAME name WINDOWCOMPAT
- DESCRIPTION 'This is my program.'
- EXETYPE OS2
- HEAPSIZE 4000
- PROTMODE
- Warning: if you specify HEAPSIZE MAXVAL and the static data is more than 64K,
- the executable won't run, but you'll get no message from the linker. If this
- happens, specify something like HEAPSIZE 4000.
-
-
- ΓòÉΓòÉΓòÉ 9. Performance hints ΓòÉΓòÉΓòÉ
-
- Standard C allows you to write over character constants:
-
- char *x="Abc";
-
- *x='X'; *(x+1)='y';
-
- If your program does not do this, put #pragma strings(readonly) at the
- beginning of the program. This way the string constants will be treated as
- constant and multiple occurrences of the same string constant will not be
- duplicated in the executable file.
-
- If the program does not examine the arguments to main, provide a dummy
- argument set up function:
-
- void _setuparg(void)
- {
- }
-
- int main(void)
- {
- ...
- It's not necessary to specify /NOE to the linker. This saves about 500 bytes
- off the executable file and a few milliseconds.
-
- Declare all functions and global variables that need not be extern (that is,
- are local to this file) to be static, e.g.,
-
- static int myfunc(void);
- static double x[10];
-