[tex-k] kpathsea and (c)web

Julian Gilbey J.D.Gilbey@qmul.ac.uk
Tue, 1 Jan 2002 01:34:47 +0000


--wac7ysb48OaltWcw
Content-Type: text/plain; charset=us-ascii
Content-Disposition: inline

On Sun, Dec 16, 2001 at 10:20:16PM +0100, Olaf Weber wrote:
> Julian Gilbey writes:
> 
> > I've just installed the latest Debian unstable version of teTeX
> > (20011202 pretest, kpathsea version 3.3.7) and see that there is now
> > support for web and cweb search paths.  I presume that web
> > (weave/tangle) has been modified to use kpathsea (I see that it's
> > linked against the library), but what about cweave?  Is there a change
> > file yet to make it use kpathsea?
> 
> Wlodek Bzyl asked me to add them when we met in Kerkrade.  For tangle
> and weave I also implemented the changes, for cweb I haven't, nor have
> I seen suitable patches.

OK, attached are patches for general comment.  They are designed not
to require CWEB to be built at the same time as Web2C (although
nothing precludes this).  Obviously, the CWEB Makefile will need
changing to specify the three change files involved ({C,T,W}CHANGES)
and also -lkpathsea in the LINKFLAGS.

   Julian

-- 
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-

     Julian Gilbey, Dept of Maths,             Debian GNU/Linux Developer
      Queen Mary, Univ. of London         see http://people.debian.org/~jdg/
   http://www.maths.qmul.ac.uk/~jdg/           or http://www.debian.org/
        Visit http://www.thehungersite.com/ to help feed the hungry
                 Also: http://www.helpthehungry.org/
--wac7ysb48OaltWcw
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment; filename="common.ch"

@x The kpathsea library defines boolean already
typedef short boolean;
@y
@z

@x We use kpathsea to find the files
reset_input()
{
@y
reset_input()
{
  string fullname;
  const_string mode = kpse_format_info[kpse_cweb_format].binmode
                      ? FOPEN_RBIN_MODE
                      : FOPEN_R_MODE;

@z

@x This actually does the work.
@ The following code opens the input files.
@^system dependencies@>

@<Open input files@>=
if ((web_file=fopen(web_file_name,"r"))==NULL) {
  strcpy(web_file_name,alt_web_file_name);
  if ((web_file=fopen(web_file_name,"r"))==NULL)
       fatal("! Cannot open input file ", web_file_name);
}
@.Cannot open input file@>
@.Cannot open change file@>
web_file_open=1;
if ((change_file=fopen(change_file_name,"r"))==NULL)
       fatal("! Cannot open change file ", change_file_name);
@y
@ The following code opens the input files.  We can't use
|kpse_open_file| directly, as this code is too intricate for that
approach.  So we have to roll our own variant.
@^system dependencies@>

@<Open input files@>=
fullname = kpse_find_file (web_file_name, kpse_cweb_format, true);
web_file = fullname ? fopen (fullname, mode) : NULL;
if (!web_file) {
  if (fullname) {
    pfatal("! Cannot open input file ", fullname);
  } else {
    fullname = kpse_find_file (alt_web_file_name, kpse_cweb_format, true);
    web_file = fullname ? fopen (fullname, mode) : NULL;
    if (!web_file) {
      if (fullname) {
        pfatal("! Cannot open input file ", fullname);
      } else {
        fatal("! Cannot open input file ", web_file_name);
      }
    }
  }
}
@.Cannot open input file@>
@.Cannot open change file@>
web_file_open=1;
fullname = kpse_find_file (change_file_name, kpse_cweb_format, true);
change_file = fullname ? fopen (fullname, mode) : NULL;
if (!change_file) {
  if (fullname) {
    pfatal("! Cannot open input file ", fullname);
  } else {
    fatal("! Cannot open change file ", change_file_name);
  }
}
@z

@x Replace the CWEBINPUTS handling with kpathsea handling
@ When an \.{@@i} line is found in the |cur_file|, we must temporarily
stop reading it and start reading from the named include file.  The
\.{@@i} line should give a complete file name with or without
double quotes.
If the environment variable \.{CWEBINPUTS} is set, or if the compiler flag
of the same name was defined at compile time,
\.{CWEB} will look for include files in the directory thus named, if
it cannot find them in the current directory.
(Colon-separated paths are not supported.)
The remainder of the \.{@@i} line after the file name is ignored.

@d too_long() {include_depth--;
        err_print("! Include file name too long"); goto restart;}

@<Include...@>=
#include <stdlib.h> /* declaration of |getenv| and |exit| */

@ @<Try to open...@>= {
  char temp_file_name[max_file_name_length];
  char *cur_file_name_end=cur_file_name+max_file_name_length-1;
  char *k=cur_file_name, *kk;
  int l; /* length of file name */

  while (*loc!=' '&&*loc!='\t'&&*loc!='"'&&k<=cur_file_name_end) *k++=*loc++;
  if (k>cur_file_name_end) too_long();
@.Include file name ...@>
  *k='\0';
  if ((cur_file=fopen(cur_file_name,"r"))!=NULL) {
    cur_line=0; print_where=1;
    goto restart; /* success */
  }
  kk=getenv("CWEBINPUTS");
  if (kk!=NULL) {
    if ((l=strlen(kk))>max_file_name_length-2) too_long();
    strcpy(temp_file_name,kk);
  }
  else {
#ifdef CWEBINPUTS
    if ((l=strlen(CWEBINPUTS))>max_file_name_length-2) too_long();
    strcpy(temp_file_name,CWEBINPUTS);
#else
    l=0;
#endif /* |CWEBINPUTS| */
  }
  if (l>0) {
    if (k+l+2>=cur_file_name_end)  too_long();
@.Include file name ...@>
    for (; k>= cur_file_name; k--) *(k+l+1)=*k;
    strcpy(cur_file_name,temp_file_name);
    cur_file_name[l]='/'; /* \UNIX/ pathname separator */
    if ((cur_file=fopen(cur_file_name,"r"))!=NULL) {
      cur_line=0; print_where=1;
      goto restart; /* success */
    }
  }
  include_depth--; err_print("! Cannot open include file"); goto restart;
}
@y
@ When an \.{@@i} line is found in the |cur_file|, we must temporarily
stop reading it and start reading from the named include file.  The
\.{@@i} line should give a complete file name with or without
double quotes.
We use the \.{kpathsea} library (in particular, the \.{CWEBINPUTS}
variable) to search for this file.
The remainder of the \.{@@i} line after the file name is ignored.

@d too_long() {include_depth--;
        err_print("! Include file name too long"); goto restart;}

@<Include...@>=
#include <stdlib.h> /* declaration of |getenv| and |exit| */

@ @<Try to open...@>= {
  char *cur_file_name_end=cur_file_name+max_file_name_length-1;
  char *k=cur_file_name;
  string fullname;
  const_string mode = kpse_format_info[kpse_cweb_format].binmode
                      ? FOPEN_RBIN_MODE
                      : FOPEN_R_MODE;

  while (*loc!=' '&&*loc!='\t'&&*loc!='"'&&k<=cur_file_name_end) *k++=*loc++;
  if (k>cur_file_name_end) too_long();
@.Include file name ...@>
  *k='\0';
  fullname = kpse_find_file (cur_file_name, kpse_cweb_format, true);
  cur_file = fullname ? fopen (fullname, mode) : NULL;
  if (cur_file) {
    cur_line=0; print_where=1;
    goto restart; /* success */
  } else {
    if (fullname) {
      pfatal("! Cannot open include file ", cur_file_name);
    } else {
      include_depth--;
      err_print("! Cannot open include file (file not found)");
      goto restart;
    }
  }
}
@z

@x System-independent way of finding basename, using kpathsea library
  while (--argc > 0) {
    if ((**(++argv)=='-'||**argv=='+')&&*(*argv+1)) @<Handle flag argument@>@;
    else {
      s=name_pos=*argv;@+dot_pos=NULL;
      while (*s) {
        if (*s=='.') dot_pos=s++;
        else if (*s=='/') dot_pos=NULL,name_pos=++s;
        else s++;
      }
@y
  while (--argc > 0) {
    if ((**(++argv)=='-'||**argv=='+')&&*(*argv+1)) @<Handle flag argument@>@;
    else {
      name_pos=(char *)xbasename(*argv);
      s=name_pos;@+dot_pos=NULL;
      while (*s) {
        if (*s=='.') dot_pos=s++;
        else s++;
      }
@z

@x Flags might be set after input files, so let's be on the safe side
  } else {
    strcpy(tex_file_name,*argv);
    strcpy(C_file_name,*argv);
    if (flags['x']) { /* indexes will be generated */
      *dot_pos=0;
      sprintf(idx_file_name,"%s.idx",*argv);
      sprintf(scn_file_name,"%s.scn",*argv);
    }
  }
@y
  } else {
    strcpy(tex_file_name,*argv);
    strcpy(C_file_name,*argv);
    *dot_pos=0;
    sprintf(idx_file_name,"%s.idx",*argv);
    sprintf(scn_file_name,"%s.scn",*argv);
  }
@z
 
@x Need to handle kpathsea --help and --version options
@ @<Handle flag...@>=
{
@y
@ @<Handle flag...@>=
{
  if (strcmp("-help",*argv)==0 || strcmp("--help",*argv)==0)
    @<Display help message and exit@>;
  if (strcmp("-version",*argv)==0 || strcmp("--version",*argv)==0)
    @<Display version information and exit@>;
@z

Use the |pfatal| function to give system error info.  Also, set
prog name for kpathsea library.

@x
@ @<Scan arguments and open output files@>=
scan_args();
if (program==ctangle) {
  if ((C_file=fopen(C_file_name,"w"))==NULL)
    fatal("! Cannot open output file ", C_file_name);
@.Cannot open output file@>
}
else {
  if ((tex_file=fopen(tex_file_name,"w"))==NULL)
    fatal("! Cannot open output file ", tex_file_name);
}
@y
@ @<Scan arguments and open output files@>=
kpse_set_progname(argv[0]);
scan_args();
if (program==ctangle) {
  if ((C_file=fopen(C_file_name,"w"))==NULL)
    pfatal("! Cannot open output file ", C_file_name);
@.Cannot open output file@>
}
else {
  if ((tex_file=fopen(tex_file_name,"w"))==NULL)
    pfatal("! Cannot open output file ", tex_file_name);
}
@z

System dependent changes.  Also, have to get rid of these
declarations, as they are handled nicely by the kpathsea library, and
this just gets in the way.
 
@x
@ We predeclare several standard system functions here instead of including
their system header files, because the names of the header files are not as
standard as the names of the functions. (For example, some \CEE/ environments
have \.{<string.h>} where others have \.{<strings.h>}.)

@<Predecl...@>=
extern int strlen(); /* length of string */
extern int strcmp(); /* compare strings lexicographically */
extern char* strcpy(); /* copy one string to another */
extern int strncmp(); /* compare up to $n$ string characters */
extern char* strncpy(); /* copy up to $n$ string characters */

@** Index.
@y
@** System dependent changes.

Here we introduce extra bits and pieces we need to work with the
\.{kpathsea} library.

@ We must include the \.{kpathsea} library.  We will also be using
|errno| and strerror.

@<Include...@>=
#include <kpathsea/kpathsea.h>
#include <errno.h>

@ We use the |pfatal| function in place of |fatal| to exit with more
specific system information.

@<Predec...@>=
void pfatal();
extern char *strerror();  /* convert |errno| into something meaningful */

@ The two parameters to |pfatal| are also strings that are essentially
concatenated to print the final error message.

@c void
pfatal(s,t)
  char *s,*t;
{
  if (*s) printf(s);
  printf(t);
  printf(": ");
  err_print(strerror(errno));
  history=fatal_message; exit(wrap_up());
}

@ Modules for dealing with help messages and version info.

@<Display help message and exit@>=
usage_help(program==ctangle ? CTANGLEHELP : CWEAVEHELP);
@.--help@>

@ Will have to change these if the version numbers change.
Unfortunately, this cannot easily be coordinated with the other two
cweb files, without putting this code into each of them.

@<Display version information and exit@>=
{
  if (program==ctangle)
    cweb_print_version_and_exit("CTANGLE", "3.62");
  else
    cweb_print_version_and_exit("CWEAVE", "3.61");
@.--version@>
}

@ And we need to initialise the strings used and define these
functions.  The banners will need changing if the version numbers of
\.{CWEAVE} or \.{CTANGLE} change; unfortunately, we cannot get hold of
the original banner strings from here as they are defined in different
files.  Phooey.

@<Other...@>=
const_string CWEAVEHELP[] = {
    "Usage: cweave [OPTIONS] WEBFILE[.w] [{CHANGEFILE[.ch]|-}] [OUTFILE[.tex]]",
    "  Weave WEBFILE with CHANGEFILE into a TeX document.",
    "  Default CHANGEFILE is /dev/null;",
    "  TeX output goes to the basename of WEBFILE extended with `.tex'",
    "  unless otherwise specified by OUTFILE; in this case, '-' specifies",
    "  a null CHANGEFILE.",
    "",
    "-b          suppress banner line on terminal",
    "-f          do not force a newline after every C statement in output",
    "-h          suppress success message on completion",
    "-p          suppress progress report messages",
    "-x          omit indices and table of contents",
    "+e          enclose C material in \\PB{...}",
    "+s          print usage statistics (if compiled with -DSTAT)",
    "--help      display this help and exit",
    "--version   output version information and exit",
    NULL
};

const_string CTANGLEHELP[] = {
    "Usage: ctangle [OPTIONS] WEBFILE[.w] [{CHANGEFILE[.ch]|-}] [OUTFILE[.tex]]",
    "  Tangle WEBFILE with CHANGEFILE into a C/C++ program.",
    "  Default CHANGEFILE is /dev/null;",
    "  C output goes to the basename of WEBFILE extended with `.c",
    "  unless otherwise specified by OUTFILE; in this case, '-' specifies",
    "  a null CHANGEFILE.",
    "",
    "-b          suppress banner line on terminal",
    "-h          suppress success message on completion",
    "-p          suppress progress report messages",
    "+s          print usage statistics (if compiled with -DSTAT)",
    "--help      display this help and exit",
    "--version   output version information and exit",
    NULL
};

@ @<Predec...@>=
void usage_help(const_string* message);
void cweb_print_version_and_exit(const_string name, const_string version);

@ And now the code, essentially copied in a simplified form from the
Web2C distro.

@c
void usage_help(const_string* message)
{
  extern KPSEDLL char *kpse_bug_address;

  while (*message) {
    fputs(*message, stdout);
    putchar('\n');
    ++message;
  }
  putchar('\n');
  fputs(kpse_bug_address, stdout);
  exit(0);
}

@ @c
void cweb_print_version_and_exit(const_string name, const_string version)
{
  extern KPSEDLL string kpathsea_version_string; /* from kpathsea/version.c */

  printf ("%s %s\n", name, version);
  puts (kpathsea_version_string);

  puts ("Copyright (C) 1987,1990,1993,2000 Silvio Levy and Donald E. Knuth");
  puts ("Kpathsea is copyright (C) 1999 Free Software Foundation, Inc.");

  puts ("There is NO warranty.  Redistribution of this software is");
  fputs ("covered by the terms of ", stdout);
  printf ("both the %s copyright and\n", name);
  puts ("the GNU General Public License.");
  puts ("For more information about these matters, see the files");
  printf ("named COPYING and the %s source.\n", name);
  printf ("Primary authors of %s: Silvio Levy and Donald E. Knuth.\n", name);
  puts ("Kpathsea written by Karl Berry and others.\n");

  exit (0);
}

@** Index.
@z

--wac7ysb48OaltWcw
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment; filename="cweave.ch"

@x Predeclare pfatal
extern void fatal(); /* issue error message and die */
@y
extern void fatal(); /* issue error message and die */
extern void pfatal(); /* issue error message (and system error) and die */
@z

@x Replace fatal with pfatal
    fatal("! Cannot open index file ",idx_file_name);
@y
    fatal("! Cannot open index file ",idx_file_name);
@z

@x Replace fatal with pfatal
    fatal("! Cannot open section file ",scn_file_name);
@y
    fatal("! Cannot open section file ",scn_file_name);
@z

--wac7ysb48OaltWcw
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment; filename="ctangle.ch"

@x Predeclare pfatal
extern void fatal(); /* issue error message and die */
@y
extern void fatal(); /* issue error message and die */
extern void pfatal(); /* issue error message (and system error) and die */
@z

@x Replace fatal with pfatal
    if (C_file ==0) fatal("! Cannot open output file:",output_file_name);
@y
    if (C_file ==0) pfatal("! Cannot open output file:",output_file_name);
@z

--wac7ysb48OaltWcw--