								-*-Web-*-
This file, CTANGL-P.CH, is part of CWEB-p. It is a changefile written by
Hans-Hermann Bode (HHBODE@@DOSUNI1.BITNET or
hhbode@@dosuni1.rz.uni-osnabrueck.de) for CTANGLE.W that provides changes
appropriate for ANSI-C compilers and for the Turbo-C++ compiler. The latter
will become effective only if the symbol __TURBOC__ is defined, which
automatically is the case when compiled with Turbo C++. Also there is
a redefinition of |max_toks| for the SAS/C 6.0 compiler by Andreas Scherer.

This program is distributed WITHOUT ANY WARRANTY, express or implied.

The following copyright notice extends to this changefile only, not to the
masterfile.

Copyright (C) 1991, 1992 Hans-Hermann Bode

The following copyright notice extends to the SAS/C specific changes only,
not to the masterfile.

Copyright (C) 1993 Andreas Scherer

Permission is granted to make and distribute verbatim copies of this
document provided that the copyright notice and this permission notice
are preserved on all copies.

Permission is granted to copy and distribute modified versions of this
document under the conditions for verbatim copying, provided that the
entire resulting derived work is distributed under the terms of a
permission notice identical to this one.

Version history:

Version	Date		Author	Comment
a1/t1	10 Oct 1991	H2B	First attempt for CTANGLE.W 2.0.
p2	13 Feb 1992	H2B	Updated for CTANGLE.W 2.2, ANSI and Turbo
				changefiles merged together.
p3	16 Apr 1992	H2B	Updated for CTANGLE.W 2.4.
p4	21 Jun 1992	H2B	Nothing changed.
p5	18 Jul 1992	H2B	Extensions for C++ implemented.
p5a	24 Jul 1992	KG	adaptions for other ANSI C compiler
p5b	28 Jul 1992	H2B	Remaining headers converted to ANSI style.
p6	06 Sep 1992	H2B	Updated for CTANGLE.W 2.7, |dot_dot_dot|
				added, parsing of @@'\'' fixed (due to KG),
				@@<Copy an ASCII constant@@> extended,
				(nonfatal) confusion in processing short
				comments fixed.
p6a     15 Mar 1993     AS      Re-changing some of the TC stuff to SAS/C

Extensions for use with other C compiler by Klaus Guntermann (KG),
TH Darmstadt (guntermann@@iti.informatik.th-darmstadt.de).
Mainly just modified all headers of prototyped functions.

------------------------------------------------------------------------------
@x l.2
% This program by Silvio Levy is based on a program by D. E. Knuth.
% It is distributed WITHOUT ANY WARRANTY, express or implied.
% Version 2.7 --- Don Knuth, July 1992

% Copyright (C) 1987,1990 Silvio Levy and Donald E. Knuth
@y
% This program by Silvio Levy is based on a program by D. E. Knuth.
% ANSI-C conforming changes and Turbo-C++ implementation are made by
% Hans-Hermann Bode.
% The program is distributed WITHOUT ANY WARRANTY, express or implied.
% Version 2.4 --- Don Knuth, April 1992
% Version 2.4 [p5] --- Hans-Hermann Bode, July 1992
% Version 2.4 [p5a] --- Klaus Guntermann, July 1992
% Version 2.4 [p5b] --- Hans-Hermann Bode, July 1992
% Version 2.7 --- Don Knuth, July 1992
% Version 2.7 [p6] --- Hans-Hermann Bode, September 1992
% Version 2.7 [p6a] --- Andreas Scherer, March 1993

% Copyright (C) 1987,1990 Silvio Levy and Donald E. Knuth

% Changes conforming to ANSI C and Turbo C++ are
% Copyright (C) 1991, 1992 Hans-Hermann Bode
% Changes conforming to SAS/C 6.0 are
% Copyright (C) 1993 Andreas Scherer
% See the changefile CTANGL-P.CH for more information.
@z
------------------------------------------------------------------------------
@x l.25
\def\title{CTANGLE (Version 2.7)}
@y
\def\title{CTANGLE (Version 2.7 [p6a])}
@z
------------------------------------------------------------------------------
@x l.29
  \centerline{(Version 2.7)}
@y
  \centerline{(Version 2.7 [p6a])}
@z
------------------------------------------------------------------------------
@x l.33
Copyright \copyright\ 1987,\thinspace1990 Silvio Levy and Donald E. Knuth
@y
Copyright \copyright\ 1987,\thinspace1990 Silvio Levy and Donald E. Knuth
\smallskip\noindent
Copyright \copyright\ 1991,\thinspace1992 Hans-Hermann Bode
\smallskip\noindent
Copyright \copyright\ 1993 Andreas Scherer
@z
------------------------------------------------------------------------------
Activate this, if only the changed modules should be printed.
x l.46
\let\maybe=\iftrue
y
\let\maybe=\iffalse
z
------------------------------------------------------------------------------
@x l.49
This is the \.{CTANGLE} program by Silvio Levy, based on \.{TANGLE} by
D.~E. Knuth.
@y
This is the \.{CTANGLE} program by Silvio Levy, based on \.{TANGLE} by
D.~E. Knuth, with {\mc ANSI-C} conforming changes and {\mc Turbo-C++}
implementation made by Hans-Hermann Bode. {\mc SAS/C~6.0} implementation
made by Andreas Scherer.
@z
------------------------------------------------------------------------------
@x l.55
@d banner "This is CTANGLE (Version 2.7)\n"
@y
@d banner "This is CTANGLE (Version 2.7 [p6a])\n"
@z
------------------------------------------------------------------------------
@x l.73
main (ac, av)
int ac;
char **av;
@y
@<Prototypes@>@;

int main (int ac, char *av[])
@z
------------------------------------------------------------------------------
the int function main needs a return value (although it will never return here)
@x l.84
  wrap_up(); /* and exit gracefully */
@y
  wrap_up(); /* and exit gracefully */
  return(0); /* not reached, but conforming to standards */
@z
------------------------------------------------------------------------------
@x l.87
@ The following parameters were sufficient in the original \.{TANGLE} to
handle \TeX, so they should be sufficient for most applications of \.{TANGLE}.
@y
@ In the following, due to memory limitations of {\mc MS-DOS} systems, the
value of |max_toks| is reduced to~$1\over2$ of its value in the original
\.{TANGLE}, the remaining parameters were sufficient in the original
\.{TANGLE} to handle \TeX; nevertheless, even |max_toks| should be sufficient
for most applications of \.{TANGLE}. Blow the fuck out of {\mc Multiple
Sclerosis}; any decent system is capable of using the defaults.
@^system dependencies@>
@z
------------------------------------------------------------------------------
@x l.94
@d max_toks 270000 /* number of bytes in compressed \Cee\ code */
@y
@d max_toks 270000 /* number of bytes in compressed \Cee\ code */
@z
------------------------------------------------------------------------------
@x l.107
@i common.h
@y
@i common-p.h
@z
------------------------------------------------------------------------------
@x l.134
  eight_bits *tok_start; /* pointer into |tok_mem| */
@y
  eight_bits HUGE *tok_start; /* pointer into |tok_mem| */
@z
------------------------------------------------------------------------------
@x l.143
eight_bits tok_mem[max_toks];
eight_bits *tok_mem_end=tok_mem+max_toks-1;
eight_bits *tok_ptr; /* first unused position in |tok_mem| */
@y
#ifdef __TURBOC__
eight_bits HUGE *tok_mem;
eight_bits HUGE *tok_mem_end;
#else
eight_bits tok_mem[max_toks];
eight_bits *tok_mem_end=tok_mem+max_toks-1;
#endif
eight_bits HUGE *tok_ptr; /* first unused position in |tok_mem| */
@z
------------------------------------------------------------------------------
@x l.148
text_info->tok_start=tok_ptr=tok_mem;
@y
#ifdef __TURBOC__
tok_mem=allocsafe(max_toks,sizeof(*tok_mem));
tok_mem_end=tok_mem+max_toks-1;
#endif
text_info->tok_start=tok_ptr=tok_mem;
@z
------------------------------------------------------------------------------
@x l.158
name_dir->equiv=(char *)text_info; /* the undefined module has no replacement text */
@y
name_dir->equiv=(char HUGE *)text_info; /* the undefined module has no replacement text */
@z
------------------------------------------------------------------------------
According to COMMON, names_match has to be declared with 4 parameters.
@x l.164
names_match(p,first,l)
name_pointer p; /* points to the proposed match */
char *first; /* position of first character of string */
int l; /* length of identifier */
@y
boolean names_match(name_pointer p,char *first,int l,eight_bits dummy)
/* |p|: points to the proposed match */
/* |first|: position of first character of string */
/* |l|: length of identifier */
/* |dummy|: not used by \.{TANGLE} */
@z
------------------------------------------------------------------------------
@x l.170
  return !strncmp(first,p->byte_start,l);
@y
  return( (boolean)!strncmp(first,p->byte_start,l) );
@z
------------------------------------------------------------------------------
@x l.179
init_node(node)
name_pointer node;
@y
void init_node(name_pointer node)
@z
------------------------------------------------------------------------------
@x l.182
    node->equiv=(char *)text_info;
@y
    node->equiv=(char HUGE *)text_info;
@z
------------------------------------------------------------------------------
According to COMMON, init_p has to be declared with 2 parameters.
@x l.184
init_p() {}
@y
void init_p(name_pointer dummy1,eight_bits dummy2)
{}
@z
------------------------------------------------------------------------------
@x l.238
@c store_two_bytes(x)
sixteen_bits x;
@y
@c static void store_two_bytes(sixteen_bits x)
@z
------------------------------------------------------------------------------
@x l.274
  eight_bits *end_field; /* ending location of replacement text */
  eight_bits *byte_field; /* present location within replacement text */
@y
  eight_bits HUGE *end_field; /* ending location of replacement text */
  eight_bits HUGE *byte_field; /* present location within replacement text */
@z
------------------------------------------------------------------------------
@x l.312
@c push_level(p) /* suspends the current level */
name_pointer p;
@y
@c static void push_level(name_pointer p) /* suspends the current level */
@z
------------------------------------------------------------------------------
@x l.327
@c pop_level() /* do this when |cur_byte| reaches |cur_end| */
@y
@c static void pop_level(void) /* do this when |cur_byte| reaches |cur_end| */
@z
------------------------------------------------------------------------------
@x l.359
@c get_output() /* sends next token to |out_char| */
@y
@c static void get_output(void) /* sends next token to |out_char| */
@z
------------------------------------------------------------------------------
@x l.387
  if ((a+name_dir)->equiv!=(char *)text_info) push_level(a+name_dir);
@y
  if ((a+name_dir)->equiv!=(char HUGE *)text_info) push_level(a+name_dir);
@z
------------------------------------------------------------------------------
@x l.437
@c flush_buffer() /* writes one line to output file */
@y
@c static void flush_buffer(void) /* writes one line to output file */
@z
------------------------------------------------------------------------------
@x l.488
phase_two () {
@y
static void phase_two(void) {
@z
------------------------------------------------------------------------------
@x l.573
@c out_char(cur_char)
eight_bits cur_char;
@y
@c static void out_char(eight_bits cur_char)
@z
------------------------------------------------------------------------------
@x l.576
  char *j; /* pointer into |byte_mem| */
@y
  char HUGE *j; /* pointer into |byte_mem| */
@z
------------------------------------------------------------------------------
@x l.611
case or_or: C_putc('|'); C_putc('|'); out_state=misc; break;
@y
case or_or: C_putc('|'); C_putc('|'); out_state=misc; break;
case colon_colon: C_putc(':'); C_putc(':'); out_state=misc; break;
case period_ast: C_putc('.'); C_putc('*'); out_state=misc; break;
case minus_gt_ast: C_putc('-'); C_putc('>'); C_putc('*'); out_state=misc;
    break;
case dot_dot_dot: C_putc('.'); C_putc('.'); C_putc('.'); out_state=misc;
    break;
@z
------------------------------------------------------------------------------
@x l.689
@c eight_bits skip_ahead() /* skip to next control code */
@y
@c static eight_bits skip_ahead(void) /* skip to next control code */
@z
------------------------------------------------------------------------------
@x l.704
speed until finding the end-comment token \.{*/} or a newline, in which
@y
speed until---for a comment introduced by \.{/*}---finding the end-comment
token \.{*/} or a newline, in which
@z
------------------------------------------------------------------------------
@x l.708
commands inserted into the C file by the output routines become useless.
@y
commands inserted into the C file by the output routines become useless.
A short comment in \Cpp, on the other hand, always is delimited by a
newline. The differentiation between the two comment styles is made by the
boolean argument |long_style|.
@z
------------------------------------------------------------------------------
@x l.715
skip_comment() /* skips over comments */
@y
static boolean skip_comment(boolean long_style) /* skips over comments */
@z
------------------------------------------------------------------------------
In the following, |get_line| will not be called at the end of a short
comment, since this would cause some (nonfatal) confusion with preprocessor
commands.
@x l.720
    if (loc>limit)
      if(get_line()) return(comment_continues=1);
      else{
        err_print("! Input ended in mid-comment");
@.Input ended in mid-comment@>
        return(comment_continues=0);
      }
    c=*(loc++);
    if (c=='*' && *loc=='/') { loc++; return(comment_continues=0); }
@y
    if (loc>limit) {
      if (long_style) {
        if(get_line()) return(comment_continues=1);
        else{
          err_print("! Input ended in mid-comment");
@.Input ended in mid-comment@>
          return(comment_continues=0);
        }
      }
      else return(comment_continues=0);
    }
    c=*(loc++);
    if (long_style && c=='*' && *loc=='/') {
      loc++; return(comment_continues=0);
    }
@z
------------------------------------------------------------------------------
@x l.754
@c eight_bits get_next() /* produces the next input token */
@y
@c static eight_bits get_next(void) /* produces the next input token */
@z
------------------------------------------------------------------------------
@x l.769
    if (comment_continues || (c=='/' && *(loc+1)=='*')) {
      skip_comment(); /* scan to end of comment or newline */
@y
    if (comment_continues || (c=='/' && (*(loc+1)=='*' || *(loc+1)=='/'))) {
      skip_comment(comment_continues||*(loc+1)=='*');
          /* scan to end of comment or newline */
@z
------------------------------------------------------------------------------
@x l.791
\.{--}, \.{->}, \.{>=}, \.{<=}, \.{==}, \.{<<}, \.{>>}, \.{!=}, \.{||} and
\.{\&\&}.  The compound assignment operators (e.g., \.{+=}) are 
@y
\.{--}, \.{->}, \.{>=}, \.{<=}, \.{==}, \.{<<}, \.{>>}, \.{!=}, \.{||},
\.{\&\&}, \.{::}, \.{.*}, \.{->*}, and \.{...}.  The compound assignment
operators (e.g., \.{+=}) are
@z
------------------------------------------------------------------------------
@x l.801
    else if (*loc=='>') compress(minus_gt); break;
@y
    else if (*loc=='>') if (*(loc+1)=='*') {loc++; compress(minus_gt_ast);}
                        else compress(minus_gt); break;
  case '.': if (*loc=='*') {compress(period_ast);}
            else if (*loc=='.' && *(loc+1)=='.') {
              loc++; compress(dot_dot_dot);
            }
            break;
  case ':': if (*loc==':') compress(colon_colon); break;
@z
------------------------------------------------------------------------------
Parse @@'\'' properly (due to KG).
@x l.907
@ @<Scan an ASCII constant@>= 
  id_first=loc;
  if (*loc=='\\') loc++;
@y
@ We have to pay special attention to ASCII constants which are supposed to
contain the special characters backslash and single quote.

@<Scan an ASCII constant@>=
  id_first=loc;
  if (*loc=='\\') {
     if (*++loc=='\'') loc++;
  }
@z
------------------------------------------------------------------------------
We store the position just after the constant in |id_loc| for use in @@<Copy
an ASCII constant@@>.
@x l.914
@.String didn't end@>
    }
  }
  loc++;
@y
@.String didn't end@>
    }
  }
  id_loc=loc++;
@z
------------------------------------------------------------------------------
@x l.1013
@ @c scan_repl(t) /* creates a replacement text */
eight_bits t;
@y
@ @c static void scan_repl(eight_bits t) /* creates a replacement text */
@z
------------------------------------------------------------------------------
According to COMMON, a dummy third argument is supplied to id_lookup.
@x l.1041
{int a=id_lookup(id_first,id_loc)-name_dir; app_repl((a / 0400)+0200);
@y
{int a=id_lookup(id_first,id_loc,' ')-name_dir; app_repl((a / 0400)+0200);
@z
------------------------------------------------------------------------------
Again, according to COMMON, a dummy third argument is supplied to id_lookup.
@x l.1045
case identifier: a=id_lookup(id_first,id_loc)-name_dir; app_repl((a / 0400)+0200);
@y
case identifier: a=id_lookup(id_first,id_loc,' ')-name_dir; app_repl((a / 0400)+0200);
@z
------------------------------------------------------------------------------
We add some escape sequences that should be recognized at least by C++
compilers. Care is taken that compilers who don't recognize them still produce
the right character.
@x l.1107
    case '0':c='\0';break;
@y
    case 'a':c='\7';break;
    case '?':c='?';break;
    case 'x':c=0;
      while (++id_first<id_loc)
        if (isdigit(*id_first)) c=16*c+(*id_first-'0');
        else if (isxdigit(*id_first)) c=16*c+(toupper(*id_first)-'A'+10);
        else err_print("! Illegal hexadecimal digit");
@.Illegal hexadecimal digit@>
      break;
    case '0':case '1':case '2':case '3':case '4':case '5':case '6':case '7':
      c=*id_first-'0';
      while (++id_first<id_loc)
        if (isdigit(*id_first)&&*id_first<='7') c=8*c+(*id_first-'0');
        else err_print("! Illegal octal digit");
@.Illegal octal digit@>
      break;
@z
------------------------------------------------------------------------------
@x l.1134
@c scan_module()
@y
@c static void scan_module(void)
@z
------------------------------------------------------------------------------
Once again, according to COMMON, a dummy third argument is supplied to
id_lookup.
@x l.1161
  app_repl(((a=id_lookup(id_first,id_loc)-name_dir) / 0400)+0200); /* append the lhs */
@y
  app_repl(((a=id_lookup(id_first,id_loc,' ')-name_dir) / 0400)+0200); /* append the lhs */
@z
------------------------------------------------------------------------------
@x l.1199
else if (p->equiv==(char *)text_info) p->equiv=(char *)cur_text;
@y
else if (p->equiv==(char HUGE *)text_info) p->equiv=(char HUGE *)cur_text;
@z
------------------------------------------------------------------------------
@x l.1208
@ @c phase_one() {
@y
@ @c static void phase_one(void) {
@z
------------------------------------------------------------------------------
@x l.1220
print_stats() {
@y
void print_stats(void) {
@z
------------------------------------------------------------------------------
@x l.1222
  printf("%d names (out of %d)\n",name_ptr-name_dir,max_names);
  printf("%d replacement texts (out of %d)\n",text_ptr-text_info,max_texts);
  printf("%d bytes (out of %d)\n",byte_ptr-byte_mem,max_bytes);
  printf("%d tokens (out of %d)\n",tok_ptr-tok_mem,max_toks);
@y
#ifdef __TURBOC__
  printf("%ld names (out of %ld)\n",name_ptr-name_dir,(long)max_names);
  printf("%ld replacement texts (out of %ld)\n",text_ptr-text_info,(long)max_texts);
  printf("%ld bytes (out of %ld)\n",byte_ptr-byte_mem,(long)max_bytes);
  printf("%ld tokens (out of %ld)\n",tok_ptr-tok_mem,(long)max_toks);
#else
  printf("%d names (out of %d)\n",name_ptr-name_dir,max_names);
  printf("%d replacement texts (out of %d)\n",text_ptr-text_info,max_texts);
  printf("%d bytes (out of %d)\n",byte_ptr-byte_mem,max_bytes);
  printf("%d tokens (out of %d)\n",tok_ptr-tok_mem,max_toks);
#endif
@z
------------------------------------------------------------------------------
@x l.1229
@* Index.
@y
@*Function declarations. Here are declarations---conforming to {\mc ANSI~C}---of
all functions in this code, as far as they are not already in |"common.h"|.

@<Proto...@>=
static void store_two_bytes(sixteen_bits x);
static void push_level(name_pointer p);
static void pop_level(void);
static void get_output(void);
static void flush_buffer(void);
static void phase_two(void);
static void out_char(eight_bits cur_char);
static eight_bits skip_ahead(void);
static boolean skip_comment(boolean long_style);
static eight_bits get_next(void);
static void scan_repl(eight_bits t);
static void scan_module(void);
static void phase_one(void);

@ Finally, we include \.{string.h} for |strncmp| and |strncpy| and
\.{fcntl.h} for |write|.

@<Include...@>=
#include <string.h>
#include <fcntl.h>

@* Index.
@z
------------------------------------------------------------------------------
