About PtoC

This is yet another Pascal to C/C++ converter. The primary idea of this converter is to produce readable and supportable code which preserves style of original code as far as possible.

Converter recognizes Pascal dialects which are compatible with Turbo Pascal 4.0/5.0 and ISO Pascal standard - IEC 7185:1990(E) (including conformant arrays). At this moment it was tested with Turbo Pascal, Oregon Pascal, Sun Pascal and HP Pascal.

Converter can produce both C++ and C output. Using of C++ language allows to encapsulate some Pascal types and constructions into C++ classes. So mapping between Pascal and C++ becomes more direct then between Pascal and C. I use C++ templates to implement Pascal arrays and files. Special template classes are used for conformant arrays. C++ like streams are used to implement Pascal IO routines. The same runtime library is used both for C and C++.

Now PtoC recognizes Turbo Pascal's extensions, such as units, strings, some special types and operations. Turbo Pascal extensions are supported only for C++ language.

At this moment PtoC successfully converts more than 400,000 lines of Oregon Pascal to C (from RSX to OpenVMS). To test C++ translation and conversion of Turbo Pascal extensions I convert BGIDEMO.PAS and LISTER.PAS files from Turbo Pascal distribution and also convert some numeric programs written on Turbo Pascal by my friends. To check quality of conversion please look in file bgidemo.cxx which was produces from original Borland bgidemo.pas without any manual changes. Moreover it is possible to compile it, link with WinBGI library and run it under Windows or X-Windows.

PtoC installation

To build PtoC just try make. Converter consists of two executable files: ptoc - converter itself and cganal - analyzer of call graph, runtime library libptoc.a, configuration file for converter ptoc.cfg, Pascal header for converter ptoc.pas (tptoc.pas for Turbo Pascal) and include file for all converted sources ptoc.h. To run converter you should only specify path to directory with ptoc and cganal. To compile and link converted files you should specify for compiler and linker path to header file ptoc.h and library libptoc.a.

I compile PtoC at Unix with GCC or CXX (Digital C++ compiler). I hope that many other C++ compilers also can do it. In MS-Windows I use Microsoft Visual C++ 5.0. You should either explicitly specify name of makefile: nmake -f makefile.mvc or use make.bat which do exactly the same. Also makefile for Borland C++ makefile.bcc is prepared. To invoke Borland linker issue command make.exe -f makefile.bcc (.exe extension is significant, otherwise make.bat will be executed)

I have used converter in following way:

rm */*.[ch] call.grp
for name in */*.pas
do
$(PTOC_DIR)ptoc -I include -h -in $name -c -analyze -intset -init -unsigned 
done
$(PTOC_DIR)cganal
In directory examples there are several Pascal files and makefile which converts and compiles this files. Examples will be build also by issuing make command. You can look in this makefile at the examples of using PTOC and compiling converted code. To compile sources produced from Turbo Pascal files, do not forget to specify -DTURBO_PASCAL option.

Directory WinBGI contains source and header file for BGI emulator for MS-Windows as well as makefile for Microsoft Visual C++ (makefile.mvc) and Borland C++ (makefile.bcc) to build this library. Directory Xbgi contains sources of BGI emulator for X-Windows.

Directory "vms" contains VMS specific versions of Pascal runtime library emulation module "io.c".

If you want to make some changes in scanner or parser you need GNU bison and flex. For MS-Windows you can download this tools for example from flexbison.zip. File produced by GNU flex contains reference to unistd.h. At Windows you can either remove this reference or create file with this name.

Pascal runtime library emulation

Arrays

All Pascal arrays in C are stored as zero based (first element has offset 0). Low value of Pascal array bounds is subtracted from index expression. When array is passed to conformant array parameter or to input/output functions Pascal array bounds are passed before array pointer. Low bound is always passed as it was mentioned in Pascal array definition (symbolic constant or integer literal). High bound is calculated depending on value of low bound (if it is explicit constant) and whether variable is formal parameter. if variable is not formal parameter and low bound is either 0 or 1 macro items(x) is used to calculate high array bound:
    #define items(x) (sizeof(x)/sizeof(*(x)))

      procedure foo(a : array [l1..h1,l2..h2:integer] of char); external;
      var a : array [1..10,-10..10] of char; 
      begin foo(a); end;    

    ==>

      foo(const int l1, const int h2, const int l2, const int h2,
          char* arr);
      char a[10][21];
      { foo(1, items(a), -10, items(*a), *a); }
If left operand is not formal parameter and hence 'sizeof' operation can be used to obtain array size two macros are used to copy and compare array:
      #define arrcmp(a,b) memcmp(a, b, sizeof(a))
      #define arrcpy(a,b) memcpy(a, b, sizeof(a))
If left operand is formal parameter sizeof operator is applied to it's type:
   
      foo(str5 s) { 
         memcpy(s, "12345", sizeof(str5));
      }
When string is passed as actual parameter for conformant array macro array(s) is used to calculate bounds of string:
      #define array(s)  1, sizeof(s)-1, (s)  
PtoC provides special type zero_terminated_string for passing zero terminated strings to C functions. Consider for example the following definition of function:
      function putenv(s : zero_terminated_string); external;
This function can be called in the following way:
    procedure foo(a : array [1..10] of char);
    begin
      putenv('VERSION=1');
      putenv(a);
    end;

==>

    void foo(array<1,10,char> a) 
    { 
      putenv("VERSION=1");
      putenv(lpsz(a));
    }
Function lpsz(a) converts array to zero terminated string. This function use static circular buffers for coping array elements to it and appending '\0' symbols at the end.

In C++ arrays are implemented by following template C++ classes:

array<low_bound, high_bound, type>
One dimensional array with fixed bounds.
conf_array<type>
Conformant array. for more details.
matrix<low_1, high_1, low_2, high_2, type>
Two dimensional array with fixed bounds.
conf_matrix<type>
Conformant two dimensional array.
See Passing parameters in C++ for more details about passing of array parameters.

Methods of classes array, conf_array, matrix, conf_matrix performs bounds checking by means of assert() statement. If, for example, array index is out of bounds, assertion will fail and program will be abnormally terminated. To avoid asserts overhead you should define NDEBUG macro (pass -DNDEBUG options to the C++ compiler).

Sets

By default converter use single set type for all Pascal sets. This set type can handle sets with card up to 256 elements (consequently size of set is 256/8 = 32 bytes). Following functions implement Pascal operations with sets:
      boolean subset(set a, set b); /* if a is subset of b */
      boolean inset(SetElemType elem, set s);
      boolean equivalent(set a, set b); 
      set     join(set a, set b);
      set     difference(set a, set b);
      set     intersect(set a, set b);
There is a special constructor for set constants: setof(). This function takes varying number of arguments each of them is either set element or range of elements, defined by macro range(a,b). List of elements should be terminated with eos (end of set) constant:
      s := [' ', '!', '?', '_', '0'..'9', 'a'..'z'];  

    ==>

      s = setof(' ', '!', '?', '_', range('0','9'), range('a','z'), eos); 
In C++ work with Pascal sets is encapsulated in set_template class. Instantiation of this class with constant MAX_SET_CARD as parameter is used as standard Pascal set. Sets of enumerations are created by set_of_enum(e) macro:
typedef set_template<MAX_SET_CARD> set;

#define set_of_enum(e) set_template<last_##e>

Mathematical functions

This is one to one correspondence between Pascal and C mathematical functions:

Pascal C
sin sin
cos cos
tan tan
arctan atan
ln log
sqrt sqrt

Functions trunc(), pred(), succ(), bitsize(), odd(), chr(), ord() are implemented by macros in the following way:

      #define trunc(x)  ((integer)(x))
      #define pred(type,x) ((type)((x) - 1))
      #define succ(type,x) ((type)((x) + 1))
      #define bitsize(x) (sizeof(x)*8)
      #define odd(x) ((x) & 1)
      #define chr(n) ((char)(n))
      #define ord(c) ((int)(unsigned char)(c))
Round function is implemented as
                        trunc(x+0.5)   x >= 0
            round(x) = 
                        trunc(x-0.5)   x < 0
Pascal function size(x) is replaced with C operator sizeof(x).

Input/Output

We use standard C io library to emulate Pascal input/output. Pascal file type is emulated by C macro file(r) which defines structure containing file descriptor and current record. There are following fields in file descriptor: pointer to C FILE structure, file name, last IO-operation error status, open mode, file status (and FAB pointer for OpenVMS system). Special macros are used for all Pascal file accessing procedures which scatter file structure fields and call corresponding functions. Bellow there is a table specifying mapping between Pascal functions, macros and C functions:

Mapping between Pascal and C I/O functions
Pascal C macro C function
rewrite rewrite pio_rewrite_file
reset reset pio_reset_file
get get pio_get_record
put put pio_put_record
eof eof pio_check_end_of_file
read sread pio_read_record (1)
write swrite pio_write_record (1)
close close pio_close
seek seek pio_seek_file
rename rename pio_rename_file
break flush pio_flush_file (2)
delete delete_file pio_delete_file (2)
iostatus iostatus pio_iostatus (2)
ioerror ioerror pio_ioerror (2)
noioerror noioerror pio_ignore_error (2)
- scopy pio_copy_record (3)
- access pio_access_recprd(3)
- store pio_store_record (3)

  1. read and write with first file parameter
  2. Oregon Pascal extensions
  3. This macros are used for translation of some Pascal constructions.
Let the following variable are defined:
      type rec = record ... end;
      var f, g : file of rec;  
          r : rec;
Then translation translation for the following construction will be:
      r := f^;
      f^ := r;
      f^ := g^;

    ==>

      r = *access(f); 
      store(f, r); 
      scopy(f, g); 
We use lazy evaluation strategy for implementing Pascal file access. Current record is red from disc only when it is accessed. To handle current state of current record two flags are used: fs_record_defined and fs_next_pos. First flag is used to mark record which is either red from disk or was assigned a value. Flag fs_next_pos is set when pointer in file is moved to position after current record. There is the following invariant: flag fs_next_pos is set only when flag fs_record_defined is set. A table below shows state of this flags after execution of some functions:

Flags settings
function fs_next_pos fs_record_defined
pio_rewrite_file 0 0
pio_reset_file 0 0
pio_get_record 0 0
pio_put_record 0 0
pio_read_record 0 0
pio_write_record 0 0
pio_seek_file 0 0
pio_copy_record 0 0
pio_access_record 1 1
pio_store_record 0 1

Pascal write and read procedures working with text files are replaced with twrite and tread C functions when first parameter is file and cwrite and cread when console input/output is used. This functions receive printf-like format string and varying number of arguments. Format string can include arbitrary text and format specifiers:

Format specifiers for read and write operations
type read write
integer %i %i

%<width>i

real %f %f

%<width>f

%<width>.<precision>f

char[]

(array of char)

%s %s

%<width>s

char*

const ok := 'ok'

(zero terminated string)

- %z

%<width>z

short

unsigned short

[-32768..32767]

[0..65535]

%W -
char

unsigned char

[-128..127]

[0..255]

%B -

where <width> and <precision> is either literal specified in format string either symbol '*'. In former case value of this qualifiers is specified AFTER (unlike C) corresponding parameter:

      write(x:5:2);        ==>        cwrite("%5.2f",x);
      write(x:w:p);        ==>        cwrite("%*.*f",x,w,p);
For symbols not preceding with % action performed by read and write are:

read write
if character is '\n' then skip all input character until newline character is reached;

otherwise character is compared with next input character and if not equal read operation fails

just output character

To output % character this symbol should be included in the format string twice.

Procedures writeln() and readln() are implemented by the same functions but symbol '\n' is appended to the formating string.

Array arguments are passed to read and write function with low and high bound specified before array pointer:

      procedure foo(str : packed array [1..100] of char); 
      begin
        writeln('str = ',str);
      end;

    ==> 
        
      void foo(char str[100]) { 
        writec("str = %s\n", 1, 100, str); 
      }
In C++ template class file<type> is used as wrapper for these C pio_ functions. PtoC uses streamio like interface for converting Pascal IO operation in C++:
      type 
        rec = record
	  code : integer;
	  name : array [1..10] of char;
        end;
      const
        pi = 3.14;
      var
        f : file of rec;
	r : rec;
      begin
        writeln('Hello world');
	writeln('pi = ', pi:10:5);
	write(f, r);
	read(f, r);
	r := f^;
	f^ := r;
	readln;
      end.

  ==>
  
      struct rec { 
          integer code;
          array<1,10,char> name;
      };
      const real pi = 3.14;
      file f;
      rec r;

      main() 
      { 
        output << "Hello world" << NL;
	output << "pi = " << format(pi, 10, 5) << NL;
        f << r;
        f >> r;
        r = *f; 
        store(f, r);
	input >> NL;
	return EXOT_SUCCESS;
     }  

The following table summirize rules of translation Pascal IO constructions to C++:
Pascal construction C++ construction
write(expr1, expr2, ..., exprN)

writeln(expr1, ..., exprN)

write(f, expr1, ..., exprN)

writeln(f, expr1, ..., exprN)

output << expr1 << expr2 << ... << exprN;

output << expr1 << ... << exprN << NL;

f << expr1 << ... << exprN;

f << expr1 << ... << exprN << NL;

read(lvalue1, lvalue2, ..., lvalueN)

readln(lvalue1, ..., lvalueN)

read(f, lvalue1, ..., lvalueN)

readln(f, lvalue1, ..., lvalueN)

input >> lvalue1 >> lvalue2 >> ... >> lvalueN;

input >> lvalue1 >> ... >> lvalueN >> NL;

f >> lvalue1 >> ... >> lvalueN;

f >> lvalue1 >> ... >> lvalueN >> NL;

write(string_expr:width)

write(integer_expr:width)

write(real_expr:width:precision)

output << format(string_expr, width);

output << format(integer_expr, width);

output << format(real_expr, width, precision);

file_variable^ *file_variable
file_variable^ := expr store(file_variable, expr);

BGI emulation

PtoC now provides emulation libraries of Borland Graphics Interface (BGI) for X-Windows and Windows-95/NT are included in this distribution (BGI emulators can be also used without converter for C programs using BGI). I found source code of BGI emulator for X-Windows in Internet, so I only have to do some changes and fix few bugs. Unfortunately this emulation library is not fully completed and tested, also not all BGI functionality is supported. And BGI emulator for MS-Windows I created myself (in Internet I found only commercial products). I called this library WinBGI.

WinBGI strictly emulates most of BGI functions (except using of non-standard drivers). Also may be mapping of fonts is not correct. But as far as sources are also available, you can easily customize them for your application. Unfortunately direct work with palette colors (setpalette, setbkcolor, write and putimage modes other than COPYPUT) is supported only for 256-colors Windows mode. Also I have used this library for only few programs (bgidemo is certainly the most complex one) so I can't guaranty that all functions always work properly. I am also sorry for the lack of parameter checking in WinBGI functions. So summarizing all above:

WinBGI advantages:

  1. Allows you to run your old Turbo-C DOS applications in 32-bit mode in normal windows. So you can easily overcome all 64Kb limitations and getting 32-bit application by simple recompilation !
  2. Graphics is much faster with WinBGI (because native Win32 API is used with minimal emulation overhead) in comparison with original application running in DOS session under Windows (especially at my PPro-200 with NT). Also it seems to me that some things (like switching of graphical pages) are not working properly in DOS mode under Windows-NT.
  3. You can use WinBGI for creating non-event driven graphical applications. For example if you want to write a program which only draws graphic of functions, it is not so easy to do with windows. You have to handle REDRAW messages, create timers to output next graphics iteration... It seems to me that BGI is much more comfortable for this purposes: you just draw lines or points and do not worry about window system at all...

WinBGI shortcomings:

  1. Handling of Windows events is done in BGI functions kbhit(). getch() and delay(). So to make your application work properly You should periodically call one of this functions. For example, the following program will not work with WinBGI:
            initgraph(&hd, &hm, NULL);
            while (1) putpixel(random(640), random(480), random(16));
            closegraph();
      
    
    Correct version of this program is:
            initgraph(&hd, &hm, NULL);
            while (!kbhit()) putpixel(random(640), random(480), random(16));
            closegraph();
    
  2. To handle REDRAW message WinBGI has to perform drawing twice: at the screen and in the pixmap which can be used while redrawing. I find that speed of drawing is still very fast but if you want to make it even faster you can assign 0 to global variable bgiemu_handle_redraw. In this case drawing is performed only at the screen but correct redrawing is not possible. If your application makes some kind of animation (constantly updates pictures at the screen) then may be storing image in the pixmap is not necessary, because your application will draw new picture instead of old one.
  3. Work with palette is possible only in 256-colors Windows mode. I don't know how to solve this problem with Win32 (I am not going to use DirectX).
  4. It is still not so good tested and not all BGI functionality is precisely simulated. I am hope that current version of WinBGI can satisfy requirements of most simple Turbo-C graphics applications.

By default WinBGI emulates VGA device with VGAHI (640x480) mode. Default mode parameter can be changed using bgiemu_default_mode variable. Special new mode VGAMAX is supported by WinBGI, causing creation of maximized window. To use this mode you should either change value of bgiemu_default_mode variable to VGAMAX and specify DETECT device type, or specify VGA device type and VGAMAX mode.

I am using Microsoft Visual C++ 5.0 to compile this application. To build library and BGIDEMO example you should only issue command nmake -f makefile.mvc. As a result you will have library winbgi.lib, header file graphics.h.

Structure of converter

Below there is a short description of converter itself:

Nested functions and call graph analysis

Converter can perform global call graph analyze in order to recognize non-recursive functions and making static variables of such functions which are accessed by nested functions. If you specify -analyze option, converter appends to file "call.grp" information about callers and callees. After conversion of all files special utility cganal can be used to produced transitive closure of call graph and output list of recursive procedures in file "recur.prc". When you run converter once again (with -analyze option) information from this file is used to mark recursive procedures. This approach greatly increase readability of program as no extra arguments need to be passed to nested functions.

Resolving name conflicts

Resolving of names conflicts is controlled by file ptoc.cfg which is loaded by converter at startup. This file specifies reserved symbols (C and C++ keywords), names of functions from C standard library, names of macros defined by converter, and mapping of names for some functions from pascal runtime.

Passing parameters in C

When converter produces C code, it doesn't copy arrays which are passed by value. Instead of this converter declare such arrays as const, so any attempt to modify contents of such array cause C compiler warning or error. It seems to me, that there are usually few places in program where procedure modifies array which is passed by value. As a rule absence of VAR qualifier means that procedure only access but not modify contents of the array. So we decide that efficient generation of this most common is more important then some amount of manual job which is necessary to correct places where array has to be copied. You should only rename formal parameter, create local variable with original name and copy value to it:
        foo(str20 const name) { 
            ...
        }  

=>

        foo(str20 name_) { 
            str20 name;

            memcpy(name, name_, sizeof(name));
            ...
        }

Passing parameters in C++

As far as Pascal arrays are represented in C++ by special class, there is no problem with array parameter passing as in C. But as far as arrays passed by value are very rarely modified in called procedure, PtoC optimizes passing of arrays by value. If array parameter, passed by value, is not changed in procedure and is not passed by reference to another procedure, then PtoC doesn't create copy of the array. This optimization can be suppressed by -copy option. When this option is specified, PtoC strictly emulates Pascal call semantic for arrays passed by value (always create copy of the array). I don't know reasons of using this options.

PtoC implements conformant array by template class conf_array. PtoC uses special macro copy_conformant_array() to create copy of conformant array passed by value when this array is modified within procedure or option -copy is specified. This macro uses alloca() function from C library, which allocates space from the system stack.

PtoC uses macro as(type, string-constant) for passing string constant to the parameter of array of char type. As far as PtoC wants to make it possible to initialize arrays by means of C aggregate construction {...}, it is impossible for array class to have constructor. That is why macro as(), which calls method array::make(char const* str), is used for passing string constant as parameter. If size of string constant is less than size of target parameter, then string is padded with spaces. If size of string constant is greater than size of target parameter, then string is truncated to the size of parameter.

Calling of C functions

Sometimes C functions need to be called from Pascal code. Sun Pascal has special "EXTERNAL C" qualifier for C procedures called from Pascal. PtoC recognize this qualifier and treat it as C (not C++) function declaration. There are some specific items of conversion of declarations and calls of such functions: The following examples illustrate these items:
type smallstr = array [1..64] of char;

procedure foo(var x : integer; a : smallstr; b : zero_terminated_string); 
external c; 

var 
   i : integer;
   s : smallstr;
begin
   foo(i, s, s);
   foo(i, 'abc', 'xyz');
end.

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

typedef array<1,64,char> smallstr;

extern "C" void foo(integer* x, char*  a, char*  b);  

int main()
{
   integer i;
   smallstr s;
   
   foo(&i, s.body(), lpsz(s));
   foo(&i, "abc", "xyz");
   return EXIT_SUCCESS;
}

Array assignments

Some C++ compilers don't allow classes with any assignment operators to be members of unions (for correct implementation it is only necessary that such classes should not redefine DEFAULT assignment operator). As far as arrays can be members of variant components in Pascal, converter can generate code without using of assignment operator for string and character constants. If your specify -assign option, converter will use assign(str) method of array instead of operator = for assignment of string and character constants to array. But PtoC still use default operator= generated by compiler for assignment of one array to another. To compile code produced with -assign option, pass -DNO_ARRAY_ASSIGN_OPERATOR option to C++ compiler.

PtoC translates assignment of string constant to variable of fixed array type by means of as(type, string-constant) macro, which performs conversion of string constant to the type of destination variable. If size of string constant is less than the size of the destination variable, then string is padded with spaces. If size of string constant is greater than size of the destination variable, then the string is truncated to the size of destination variable,

Conversion of Turbo Pascal strings

For conversion of Turbo Pascal classes string and varying_string were designed to represent correspondent Pascal types. This classes have constructors and assignment operators and so they can not be initialized using C aggregates notation {} and can not be used as components of unions (GCC allows to initialize classes with constructors with {}). To avoid this problem either manually replace such places with C arrays or use option -cstring of compiler. When this option is set converter replaces type of string component of records or arrays with C pointer to zero terminated string const char*. This approach works well only when this types are used to declare constants.

Some porting problems

Representation of integer type

When your are porting application from 16-bit architecture platform you may want to preserve integer size (2 bytes). In this case you can face with two problems: one is that pointers will not more fit into such integers. Converter can't help your in this case. You should change types of some variables and records fields. And second problem is less obvious. In language C short and char operands are converted to int type before operation takes place. So if you you compare for equality variables of signed and unsigned type declared in Pascal as
      type
        word : -32768..32767
        uword : 0..65535
      var
        v1 : word;
        v2 : uword;
containing the same value (for example 40000) then result will be false (unlike original application) ! This is because variable with signed type will be converted to integer with sign extension while variable with unsigned type - without sign extension. To help to deal with this problem converter provides option -unsigned, which force converter to insert explicit type conversion in such operations. Lets look at the translation the following Pascal construction with and without -unsigned option:

Pascal C without -unsigned C with -unsigned
if v1 = v2 then ... if (v1 == v2) ... if ((uword)v1 == v2) ...

It is not recommended to use 2-byte C types for representing INTEGER and WORD Turbo Pascal types, because structures and functions of BGI emulation library deal with original C int type. Also I see no much sense in using short types for converted Turbo Pascal applications because in this case you can not receive benefits of 32-bit architecture, it is better to run original application.

Implementation of Pascal sets

Sometimes it is necessary to preserve original size of data structure. For example if structure is mapped to another structure by means of union (record with variants in Pascal) or is extracted from file. There are two options in converter which can help you in this case. First option is -intset, which order converter to generate short sets (2 or 4 bytes) for sets of enumeration types, Operations with short sets are implemented by macros using bit arithmetic. (so they are significantly faster than operations with universal sets). Disadvantage of using short sets is that adding elements to enumeration may cause problems in future.

Enumeration types

And another option is -smallenum. The problem is that enum type in C is treated by many compilers as integers and there are no ways to make compiler use less bytes for their representation. When you specify option -smallenum converter replaces original enumeration type definition with unsigned char or unsigned short definitions according to number of elements in enumeration. So construction
        colors = (red, green, blue);
will be translated to
        typedef unsigned char colors;
        enum {red, green, blue};        
Size of colors type will be 1. And without -smallenum size of colors type depends on compiler and usually will be equal to the size of integer (4):
        enum colors {red, green, blue};        

Style of converted sources

As was mentioned above converter tries to preserve original indentation of converted sources. But if Pascal sources are not properly aligned you can reformat produced C code using some indentation utility, for example GNU indent, which is freely distributed (GNU indent has one interesting bug: it fails to work with files with empty comments /**/ which are produced from popular Pascal {} comments. To avoid this problem just replace such comments with /* */ or something else).

Includes in ANSII Pascal

PtoC supports %include operator (used in Oregon Pascal). This operator works like C #include directive, performing text substitution. This operator can be used in one of the following forms:
%include file             { "file.pas" will be included }
%include '../../file.inc' { "../../file.inc" will be included }
%include file.con ;       { "file.con" will be included }
The statements above will be converted to
#include "file.h"
#include "../../file_inc.h"
#include "file_con.h"
If several files include the file with variables declarations, then this variables will be multiple defined. This problem can be solved either by passing option to linker to merge symbols with the name (most linkers have such option), either by using -extern option. Specifying -extern option tells the converter to prepend each variable declaration in included file by EXTERN qualifier. EXTERN is defined as extern in ptoc.h and is redefined to "" (empty string) if:

  1. included file name without extension is the same as name of converted file without extension
  2. included file name extension is ".var"

PtoC options

-I [.]
Include path (colon separated directory list). PtoC will search for included Pascal files in all specified directories.
-in
Input Pascal file. Exactly one input file should be specified for PtoC. Keyword -in can be skipped if name of the file doesn't start with minus sign.
-out
Name of output C/C++ file. If output file name is not specified, PtoC will creates file with the same name as source Pascal file with extension replaced with ".cxx" (or with one specified by -suf option).
-suf [.cxx]
Output C/C++ file name suffix.
-c
Translate into ANSI C. By default converter produce C++ output.
-assign
Do not use assignment operators for array. Use method array::assign() instead. See Array assignments
-analyze
Analyze call graph to find non-recursive functions. Makes static all variables from non-recursive functions, which are accessed from nested functions. See Nested functions and call graph analysis
-intset
Use integer types for short sets of enumerations. See Implementation of Pascal sets
-init
Call pio_initialize() function from main(). Invocation of this function performs initialization of Pascal runtime library structures. This functions must be called in VMS and for Turbo Pascal if ParamStr or ExitProc variables are used.
-smallenum
Use for enumerated types as small bytes as possible. See Enumeration types
-unsigned
Generate correct code for sign/unsigned comparisons when application is ported from 16-bit architecture to 32/64 bit platform with preserving size of integer type (2 bytes). See Representation of integer types
-h
Output only not existed header files. By default PtoC performs conversion and output of all included files. This option tells PtoC not to output existed files.
-turbo
Recognize Turbo Pascal extensions.
-cstring
Use char* type for string fields in records and arrays.
-nological
Use | and & instead of || and && for boolean operations.
-extern
Declare all variables from included files with EXTERN qualifier.
-preserve
Preserve case of identifiers. By default PtoC translates all names to lowercase. If you prefer to preserve original style of using uppercase and lowercase letters, then use this option.
-nested
Nested comments. Do not mix (* *) and { } comments. By default PtoC consider (* ... } as valid comment. Setting this option makes it possible to enclose one type of comments insize another one: (* outside comment { inside comment } *). This rule is the same as in Turbo Pascal, so this option is implicitly set by -turbo option.
-copy
This option is meaningful only for C++ conversion. When this option is set, PtoC strictly emulates Pascal call semantic for arrays passed by value (always create array copy). By default PtoC optimizes passing of array parameter by value. Array parameter is copied only if it is changed within procedure or it is passed by reference to another procedure. See Passing parameters in C++
-pascall []
Specify modifier (pascal, WINAPI...) for converted functions.
-comment_tags
Place in comments tags of Pascal variant records. By default PtoC just skip this tags and doesn't output them to C file. in comments tags of Pascal variant records. By default PtoC just skip this tags and doesn't output them to C file.
-namespace
Place Turbo Pascal units in separate namespaces:
unit foo;
interface
var 
    a : integer;
implementation
end.
---------------------------------------------
namespace foo { 
    integer a;
}
unsing namespace foo;

Known bugs