/*  
 *  This file is part of abc2ps, 
 *  Copyright (C) 1996-2007  Michael Methfessel
 *  See file abc2ps.c for details.
 */

#include <ctype.h>
#include <stdlib.h>
#include <string.h>

#include "abc2ps.h"
#include "style.h"
#include "buffer.h"
#include "util.h"
#include "format.h"
#include "pssubs.h"
#include "parse.h"

#include "subs.h"


/*  miscellaneous subroutines  */

/* ----- write_help ----- */
void write_help (int job)
{

  if (job > 0) { 

  printf ("Usage: abc2ps [flags] files..\n"
          "  - typeset music in abc code in Postscript.\n"
          "where: files   input files in abc format\n"
          "       nums    tune xref numbers, i.e. 1 3 6-7,20-\n"
          "       pats    patterns for title search\n" 
          "Tunes are selected if they match a number or a pattern.\n"
          "Flags: -o      write PostScript output (default)\n" 
          "       -L      list contents of file, no PostScript output\n"
          "       -E      produce EPSF output, one tune per file\n"
          "       -O aa   set outfile name to aaa\n"
          "       -O =    make outfile name from infile/title\n" 
          "       -I      write index to Ind.ps\n"
          "       -v nn   set verbosity level to nn\n"
          "       -T      write text file with ABC code\n"
          "       -h      show this command summary\n"
          "       -hh     show version; show accpats with -a\n");

  printf ("  Playback:\n"
          "       -a      write audio file %s.[wav|au], default is %s\n"
          "       -A      do accompaniment, choose own style\n"
          "       -Ax     do accompaniment, set style to x (%s)\n"
          "       -W      switch between .wav and .au audio format\n"
          "       -C      toggle CD quality output (44100 smp/sec, 16 bit)\n"
          "       -Q xx   set playback tempo to xx beats per minute\n"
          "       -q m/m  set audio qualities for voices/acc (m=pafcog)\n"
          "       -i      forbid chord inversions\n"
          "       -G      gchord accompaniment only, no melody\n"
          "       -X m-n  play bars m through n only\n",
          AUDIOFILE, WAV_IS_DEFAULT ? "wav" : "au", PATS);

  printf ("  Selection:\n"
          "       -e      following arguments are selectors\n"
          "       -f      following arguments are file names\n"
          "       -V str  select voices, eg. -V 1,4-5\n");

  printf ("  Format options:\n"
          "       -H      show the format parameters\n"
          "       -p      pretty output (looks better, needs more space)\n"
          "       -P      select second predefined pretty output style\n"
          "       -s xx   set scale factor for symbol size to xx\n" 
          "       -w xx   set staff width (cm/in/pt)\n"
          "       -m xx   set left margin (cm/in/pt)\n"
          "       -d xx   set staff separation (cm/in/pt)\n"
          "       -x      include xref numbers in output\n"
          "       -k nn   number every nn bars; 0 for first in staff\n"
          "       -n      include notes and history in output\n"
          "       -N      write page numbers\n"
          "       -1      write one tune per page\n"
          "       -l      landscape mode\n"
          "       -g xx   set glue mode to shrink|space|stretch|fill\n"
          "       -F foo  read format from \"foo.fmt\"\n"
          "       -D bar  look for format files in directory \"bar\"\n"
/*|       "       -X x    set strictness for note spacing, 0<x<1 \n" |*/
          "       -S kkk  set default key spec to kkk (e.g. \"bass +8\")\n"
          "       -Z aaa  add annotation at top of page\n");

  printf ("  Transposing:\n"
          "       -t n    transpose by n halftones (_n for negative number)\n"
          "       -t XX   transpose root up/down to note XX (eg. C A# ^Bb _Bb)\n"
          "               To transpose selected voices: e.g. -t^3=1,3-4\n"
          "  Line breaks:\n"
          "       -b      break at all line ends (ignore continuations)\n"
          "       -c      continue all line ends\n"
          "       -B n    put line break every n bars\n");

  printf ("  Alloc options:\n"
          "     -maxs n   set maximal number of symbols (default %d)\n"
          "     -maxv n   set maximal number of voices (default %d)\n", 
          maxSyms, maxVc);

  }

  printf ("Version: abc2ps v%s%s (%s) compiled %s\n", 
          VERSION, REVISION, VDATE, __DATE__);

}


/* ----- write_version ----- */
void write_version ()
{

/*  printf ("abc2ps v%s (%s, %s) compiled %s\n", 
          VERSION, VDATE, STYLE, __DATE__); */


/*|   printf ("abc2ps v%s.%s (%s, %s) compiled %s\n",  |*/
/*|           VERSION, REVISION, VDATE, STYLE, __DATE__); |*/

  printf ("abc2ps v%s.%s (%s) compiled %s\n", 
          VERSION, REVISION, VDATE, __DATE__);
  
  if (strlen(DEFAULT_FDIR)>0) 
    printf ("Default format directory %s\n", DEFAULT_FDIR);
  
}

/* ----- is_xrefstr: check if string ok for xref selection ---- */
int is_xrefstr (str)
char str[];
{
  
  char *c;
  c=str;
  while (*c != '\0') {
    if ((!isdigit(*c)) && (*c!='-') && (*c!=',') && (*c!=' ')) return 0;
    c++;
  }
  return 1;
}


/* ----- make_arglist: splits one string into list or arguments ---- */
int make_arglist (str, av)
char str[];
char *av[];
{
  char *q;
  int n;
  
  q=str;
  n=1;                     /* first real arg is 1, as in argv */
  for (;;) {
    while (*q==' ') q++;
    if (*q=='\0') break;
    av[n]=q;
    n++;
    while ((*q!=' ') && (*q!='\0')) q++;
    if (*q=='\0') break;
    *q='\0';
    q++;
  }
  return n;
}


/* ----- init_ops ----- */
void init_ops (job)
int job;
{
  int j;

  one_per_page         = -1;
  landscape            = -1;
  scalefac             = -1.0;
  lmargin              = -1.0;
  swidth               = -1.0;
  write_history        = -1;
  staffsep             = -1;
  dstaffsep            =  0;
  break_continues      = -1;
  continue_lines       = -1;
  include_xrefs        = -1;
  alfa_c               = -1.0;
  strict1              = -1.0;
  strict2              = -1.0;
  barnums              = -1;
  barnums_cl           = -1;
  make_index           =  0;
  make_audio           =  0;
  wav_format           =  WAV_IS_DEFAULT;
  playtempo            =  0;
  au_nomusic           =  0;
  cd_format            =  0;
  select_all           =  0;
  pb_start = pb_end    = -1;
  write_text           =  0;
  do_acc               =  0;
  strcpy (acc_pat,"NONE");

  /* new default is to make output, use -L to list */
/*|   do_mode              = DO_INDEX; |*/
/*|   make_ps              =  0; |*/
  do_mode              = DO_OUTPUT;
  make_ps              = 1;

  pagenumbers          = 0;
  strcpy (styf, "");
  ntrp = 0;
  strcpy (transpose[0], "");
  strcpy (vcselstr, "");
  strcpy (au_qstr, "");

  force_ktype     = 0;
  force_add_pitch = 0;
  for (j=0; j<MAXSVC; j++) 
    frc_ktype[j] = frc_add_pitch[j] = 0;
  
  if (job) {
    strcpy (styd, DEFAULT_FDIR);
    strcpy (outf, OUTPUTFILE);
    invert_chords        = 1;
    pretty               = 0;
    epsf                 = 0;
    choose_outname       = 0;
    gmode                = G_FILL;
    vb                   = VERBOSE0;
    search_field0        = S_TITLE;
  }
}


/* ----- ops_into_fmt ----- */
void ops_into_fmt (fmt)
struct FORMAT *fmt;
{
  if (landscape >= 0)          fmt->landscape=landscape;
  if (scalefac >= 0)           fmt->scale=scalefac;
  if (lmargin >= 0)            fmt->leftmargin=lmargin;
  if (swidth >= 0)             fmt->staffwidth=swidth;
  if (continue_lines >= 0)     fmt->continueall=continue_lines;
  if (break_continues >= 0)    fmt->breakall=break_continues;
  if (write_history >= 0)      fmt->writehistory=write_history;
  if (bars_per_line > 0)       fmt->barsperstaff=bars_per_line;
  if (include_xrefs >= 0)      fmt->withxrefs=include_xrefs;
  if (one_per_page >= 0)       fmt->oneperpage=one_per_page;
  if (alfa_c >= 0)             fmt->maxshrink=alfa_c;
  if (staffsep >= 0)           fmt->staffsep=staffsep;
  if (strict1 >= 0)            fmt->strict1=strict1;
  if (strict2 >= 0)            fmt->strict2=strict2;
  if (barnums >= 0)            fmt->barnums=barnums;
  if (playtempo > 0)           fmt->playtempo=playtempo;
  if (strlen(au_qstr)>0)       strcpy(fmt->auquality, au_qstr);
  if (strcmp(acc_pat,"NONE"))  fmt->accomp = acc_pat[0];
  if (barnums_cl >= 0)         fmt->barnums_cl = barnums_cl;
  fmt->staffsep    = fmt->staffsep    + dstaffsep;
  fmt->sysstaffsep = fmt->sysstaffsep + dstaffsep;

}


/* ----- parse_args: parse list of arguments, interpret flags ----- */
int parse_args (int ac, char *av[])
{
  int i, m, k, nsel, sel_arg, j, ok, f_pos, got_value, nn;
  char c, aaa[201], ext[41], cc, *s;
  struct KEYSTR tmp_key;   

  help_me      = 0;
  ninf         = 0;
  nsel         = 0;
  sel_arg      = 0;
  respect_case = 0;
  f_pos        = -1;
  s_field[0]   = search_field0;
  strcpy (sel_str[0], "");
  strcpy (znote, "");
  strcpy (global_transpose, "");
  global_halftones = 0;

  i=1;
  while (i < ac) {
    
    if (av[i][0] == '-') {         /* interpret a flag with '-'*/
      
      /* identify fullword options first */
      if (!strcmp(av[i],"-maxv")) {
        if ((i==ac-1) || (av[i+1][0]=='-')) rx("missing parameter for ", av[i]);
        sscanf(av[++i], "%d", &maxVc);
        if (maxVc > 100) maxVc = 100;
      }
      else if (!strcmp(av[i],"-maxs")) {
        if ((i==ac-1) || (av[i+1][0]=='-')) rx("missing parameter for ", av[i]);
        sscanf(av[++i], "%d", &maxSyms);
      }
      else if (!strcmp(av[i], "-hh")) {
        help_me = 3;
      }
#if ABC_USE_CAIRO
      else if (!strcmp(av[i],"-format")) {
        if ((i==ac-1) || (av[i+1][0]=='-')) rx("missing parameter for ", av[i]);
        outputFormat = av[++i];
      }
#endif

      else {
        m = 1;
        k = strlen (av[i]);
        while (m < k) {
          c = av[i][m];
          if      (c == 'h')  help_me = 1;                 /* simple flags */
          else if (c == 'H')  help_me = 2;     
/*|           else if (c == 'e')  sel_arg = 1; |*/
          else if (c == 'f')  {
            nsel++;
            strcpy (sel_str[nsel], "");
            if (ninf == 0) {             /* selector before first file */
              strcpy (sel_str[nsel], sel_str[nsel-1]);
              s_field[nsel] = s_field[nsel-1];
            }
            s_field[nsel] = search_field0;
            sel_arg = 0;
            f_pos = i;
          }
          else if (c == 'b')  {break_continues=1; continue_lines=0;}
          else if (c == 'c')  {continue_lines=1; break_continues=0;}
          else if (c == 'x')  include_xrefs=1;
          else if (c == '1')  one_per_page=1;
          else if (c == 'i')  invert_chords=0;
          else if (c == 'n')  write_history=1;
          else if (c == 'l')  landscape=1;
          else if (c == 'p')  pretty=1;
          else if (c == 'P')  pretty=2;
          else if (c == 'E')  epsf=1;
          else if (c == 'o')  { do_mode=DO_OUTPUT; make_ps=1; }
          else if (c == 'L')  { do_mode=DO_INDEX; make_ps=0; } 
          else if (c == 'N')  pagenumbers=1;
          else if (c == 'I')  make_index=1;
          else if (c == 'a')  { do_mode=DO_OUTPUT; make_audio=1; }
          else if (c == 'W')  wav_format = 1 - wav_format;
          else if (c == 'G')  au_nomusic = 1;
          else if (c == 'T')  write_text = 1;
          else if (c == 'C')  {
            cd_format = make_audio = make_ps = 1;
            do_mode = DO_OUTPUT;
          }

          /* -ex only with sticky arg */
          else if (c == 'e') {
            sel_arg = 1;
            respect_case = 0;
            s_field[nsel] = S_TITLE;
            if (av[i][m+1]) {
              cc = av[i][m+1];
              if      (cc == 't') s_field[nsel] = S_TITLE; 
              else if (cc == 'c') s_field[nsel] = S_COMPOSER; 
              else if (cc == 'r') s_field[nsel] = S_RHYTHM;  
              else if (cc == 's') s_field[nsel] = S_SOURCE;  
              else if (cc == 'T') { respect_case=1; s_field[nsel]=S_TITLE; }
              else if (cc == 'C') { respect_case=1; s_field[nsel]=S_COMPOSER; }
              else if (cc == 'R') { respect_case=1; s_field[nsel]=S_RHYTHM;  }
              else if (cc == 'S') { respect_case=1; s_field[nsel]=S_SOURCE;  }
              else {
                printf ("Invalid parameter for flag -e: %c\n", cc);
                return 1;
              }
            }
            break;
          }

          /* -Ax only with sticky arg */
          else if (c == 'A') {  
            if (av[i][m+1]) {
              acc_pat[0] = av[i][m+1];
              acc_pat[1] = '\0';
            }
            do_mode=DO_OUTPUT; do_acc=1; make_audio=1; make_ps=1;  
            break;  
          }  
          
          
          /* --- flags with parameters */
          else if (strchr("vsdwmgtkqAQDFYBVXOZS",c)) {  
            strlcpy (aaa, &av[i][m+1], 200);
            if ((strlen(aaa)>0) && strchr("glO",c)) {     /* no sticky arg */
              printf ("+++ Incorrect usage of flag -%c\n", c);
              return 1; 
            }
            
            got_value = 1;                            /* check for value */
            if (strlen(aaa) == 0) {
              got_value = 0;
              i++; 
              if ((i >= ac) || (av[i][0] == '-')) 
                i--;
              else {
                strlcpy (aaa, av[i], 200);
                got_value = 1;
              }
            }
            
            if (got_value && strchr("vsYB",c)) {      /* check num args */
              ok = 1;
              for (j=0; j<strlen(aaa); j++)
                if (!strchr("0123456789.",aaa[j])) ok = 0;
              if (!ok) {
                printf ("+++ Invalid parameter <%s> for flag -%c\n",aaa,c);
                return 1;
              }
            }
            

            /* --- flags which require a value */
            if (!got_value) {                   /* check value was given */
              printf ("+++ Missing parameter after flag -%c\n", c);
              return 1; 
            }

            if (c == 'S') {
              s = strchr (aaa, '=');
              if (s) {
                *s++ = 0;
                if (set_keysig (aaa, &tmp_key, 1)) 
                  rx ("Invalid argument for flag -S: ", aaa);
                for (j=0; j<MAXSVC; j++) {
                  if (str_sel (s, j+1)) {
                    frc_ktype[j]     = tmp_key.ktype;
                    frc_add_pitch[j] = tmp_key.add_pitch;
                  }
                }
              }
              else {
                if (set_keysig (aaa, &tmp_key, 1)) 
                  rx ("Invalid argument for flag -S: ", aaa);
                force_ktype     = tmp_key.ktype;
                force_add_pitch = tmp_key.add_pitch;
                for (j=0; j<MAXSVC; j++) {
                  frc_ktype[j]     = tmp_key.ktype;
                  frc_add_pitch[j] = tmp_key.add_pitch;
                }
              }
              break;
            }

            if (c == 'Z') {
              strlcpy (znote, aaa, 200);
              break;
            }

            if (c == 'k') {
              nn = 0;
              sscanf (aaa, "%d%n", &barnums, &nn); 
              if (aaa[nn] == 'c') barnums_cl = 1;
              break;
            }

            if (c == 'Q') {
              sscanf(aaa,"%f",&playtempo); 
              if (playtempo < 6) playtempo=6;
              break;
            }

            if (c == 'q') {
              strlcpy (au_qstr, aaa, 20);
              break;
            }
            
            if (c == 'V') {                               /* -V flag */
              ok=1;
              strlcpy (vcselstr, aaa, 100);
            }
            
            if (c == 'X') {                  /* -X flag now selects pb bars */
              ok=1;
              pb_start = pb_end = -1;

              if (*aaa == '-') {
                pb_start = 1;
                sscanf (aaa+1, "%d", &pb_end);
              }
              else {
                sscanf (aaa, "%d-%d", &pb_start, &pb_end);
                if (pb_end < 0) pb_end   = 100000;
                if (!strchr(aaa,'-')) pb_end = pb_start;
              }
              if (pb_start == 0) pb_start = 1;
              if (pb_end < pb_start) pb_end = pb_start;
            }

            if (c == 'O') {                               /* -O flag  */
              if (!strcmp(aaa,"=")) {
                choose_outname=1;
              }
              else {
                getext (aaa,ext);
                if (strcmp(ext,"ps") && strcmp(ext,"eps") && strcmp(ext,"")) {
                  printf ("Wrong extension for output file: %s\n", aaa);
                  return 1;
                }
                strext (outf, aaa, "ps", 1);
                choose_outname = 0;
              }
            }
            
            if (c == 'B') {
              sscanf (aaa," %d", &bars_per_line); 
              continue_lines = 0;
            }

            if (c == 't') {
              if (ntrp >= MAXTRP) rx ("Too many transpose flags","");
              strlcpy (transpose[ntrp], aaa, 20);
              if (!strchr(aaa,'=')) strlcpy (global_transpose, aaa, 20);
              ntrp++;
            }

            if (c == 'v') sscanf(aaa,"%d",&vb); 
            if (c == 's') sscanf(aaa,"%f",&scalefac);
            if (c == 'd') {
              if (aaa[0]=='+' || aaa[0]=='-') dstaffsep = scan_u(aaa);
              else staffsep = scan_u(aaa);
            }
            if (c == 'w') swidth   = scan_u(aaa);
            if (c == 'm') lmargin  = scan_u(aaa);
            if (c == 'F') strlcpy (styf, aaa, STRL1-1);
            if (c == 'D') strlcpy (styd, aaa, STRL1-1);
            if (c == 'g') {
              if      (abbrev(aaa,"shrink", 2)) gmode=G_SHRINK;
              else if (abbrev(aaa,"stretch",2)) gmode=G_STRETCH;
              else if (abbrev(aaa,"space",  2)) gmode=G_SPACE;
              else if (abbrev(aaa,"fill",   2)) gmode=G_FILL;
              else {
                printf ("+++ Bad parameter for flag -g: %s\n",aaa);
                return 1;
              }
            }
            break; 
          }
          else {
            printf ("+++ Unknown flag: -%c\n", c);
            return 1;
          }
          m++;
        }
      }
    }

    else {
      if (strstr(av[i],".fmt")) {     /* implict -F */ 
        strlcpy (styf, av[i], STRL1-1);
      }
      else {
        if (strstr(av[i],".abc") && sel_arg) {     /* file if .abc */
          nsel++;
          strcpy (sel_str[nsel], "");
          s_field[nsel] = S_TITLE;
          if (ninf == 0) {                /* selector before first file */
            strcpy (sel_str[nsel], sel_str[nsel-1]);
            s_field[nsel] = s_field[nsel-1];
        }
          sel_arg = 0;
        }
        if (is_xrefstr(av[i]) && (!sel_arg)) {    /* sel if xref numbers */
          if (i-1 != f_pos) sel_arg=1;
        }
        
        if (!sel_arg) {             /* this arg is a file name */
          if (ninf>=MAXINF) {
            printf ("+++ Too many input files, max is %d\n", MAXINF);
            return 1;
          }
          strlcpy (in_file[ninf], av[i], STRL1-1); 
          psel[ninf] = nsel;
          ninf++;
        }
        else {                             /* this arg is a selector */
          strcat(sel_str[nsel], av[i]);
          strcat(sel_str[nsel], " ");
        }
      }
    }
    i++;
  }

/*|   for (j=0; j<maxVc; j++) |*/
/*|     printf ("voice %d  ktype=%d add=%d\n", j+1, |*/
/*|             frc_ktype[j], frc_add_pitch[j]); |*/
  
  return 0;
}

/* ----- alloc_structs ----- */
/* Thanks to Henrik Norbeck for this */
void alloc_structs ()
{
  int j;

  sym = (struct SYMBOL *)calloc(maxSyms, sizeof(struct SYMBOL));
  if (sym == NULL) rx("Out of memory","");

  symv = (struct SYMBOL **)calloc(maxVc, sizeof(struct SYMBOL *));
  if (symv == NULL) rx("Out of memory","");

  for (j=0;j<maxVc;j++) {
    symv[j] = (struct SYMBOL *)calloc(maxSyms, sizeof(struct SYMBOL));
    if (symv[j] == NULL) rx("Out of memory","");
  }
  
  xp = (struct XPOS *)calloc(maxSyms+1, sizeof(struct XPOS));
  if (xp == NULL) rx("Out of memory","");
  
  for (j=0;j<maxSyms+1;j++) {
    xp[j].p = (int *)calloc(maxVc, sizeof(int));
    if (xp[j].p == NULL) rx("Out of memory","");
  }

  voice=(struct VCESPEC *)calloc(maxVc, sizeof(struct VCESPEC));
  if (voice == NULL) rx("Out of memory","");

  sym_st = (struct SYMBOL **)calloc(maxVc, sizeof(struct SYMBOL *));
  if (sym_st == NULL) rx("Out of memory","");
  
  for (j=0;j<maxVc;j++) {
    sym_st[j] = (struct SYMBOL *)calloc(MAXSYMST, sizeof(struct SYMBOL));
    if (sym_st[j] == NULL) rx("Out of memory","");
  }
  
  nsym_st = (int *)calloc(maxVc, sizeof(int));
  if (nsym_st == NULL) rx("Out of memory","");

}

#if ABC_LIBRARY
void free_structs ()
{
  int j;

  if ( sym )
    free( sym );

  if ( symv ) {
    for (j=0;j<maxVc;j++)
      free( symv[j] );
    free( symv );
  }
  
  if ( xp ) {
    for (j=0;j<maxSyms+1;j++)
      free( xp[j].p );
    free( xp );
  }
  
  if ( voice )
    free( voice );

  if ( sym_st ) {
    for (j=0;j<maxVc;j++)
      free( sym_st[j] );
    free( sym_st );
  }
  
  if ( nsym_st )
    free( nsym_st );
}
#endif

/* ----- set_page_format ----- */
int set_page_format ()
{
  int i,j;
  
  if (pretty == 1)
    set_pretty_format (&cfmt);
  else if (pretty == 2)
    set_pretty2_format (&cfmt);
  else 
    set_standard_format (&cfmt);

  i=read_fmt_file ("fonts.fmt", styd, &cfmt);
  j=0;
  if (strlen(styf)>0) {
    strext(styf,styf,"fmt",1);
    j=read_fmt_file (styf, styd, &cfmt);
    if (j == 0)  {
      printf ("\n+++ Cannot open file: %s\n", styf);
      return 0;
    }
    strlcpy (cfmt.name, styf, STRLFMT-1);
  }
  if (i || j) printf("\n");
  ops_into_fmt (&cfmt);

  make_font_list (&cfmt);
  sfmt = cfmt;
  dfmt = cfmt;
  return 1;
}


/* ----- tex_str: change string to take care of some tex-style codes --- */
/* Puts \ in front of ( and ) in case brackets are not balanced,
   interprets some TeX-type strings using ISOLatin1 encodings.
   Returns the length of the string as finally given out on paper. 
   Also returns an estimate of the string width... */
int tex_str (str,s,wid)
char *str;
char s[];
float *wid;
{
  char *c;
  int base,add,n;
  char t[21];
  float w;

  c = str;
  strcpy (s,"");
  n = 0;
  w = 0;
  while (*c != '\0') {

    if ((*c == '(') || (*c == ')'))        /* ( ) becomes \( \) */
      {sprintf(t, "\\%c", *c); strcat(s,t); w+=cwid('('); n++; }

    else if (*c == '\\') {                  /* backslash sequences */
      c++;
      if (*c == '\0') break;
      add=0;                              /* accented vowels */ 
      if (*c == '`')  add=1;    
      if (*c == '\'') add=2;
      if (*c == '^')  add=3;
      if (*c == '"')  add=4;
      if (add) {
        c++;
        base=0;
        if (*c == 'a') { base=340; if (add == 4) add=5; }
        if (*c == 'e')   base=350;
        if (*c == 'i')   base=354;
        if (*c == 'o') { base=362; if (add == 4) add=5; }
        if (*c == 'u')   base=371;
        if (*c == 'A') { base=300; if (add == 4) add=5; }
        if (*c == 'E')   base=310;
        if (*c == 'I')   base=314;
        if (*c == 'O') { base=322; if (add == 4) add=5; }
        if (*c == 'U')   base=331;
        w+=cwid(*c);
        if (base) 
          {sprintf(t,"\\%d",base+add-1); strcat(s,t); n+=1; }
        else
          {sprintf(t,"%c",*c); strcat(s,t); n+=1; }
      }

      else if (*c == ' ')                               /* \-space */
        { strcat(s," "); w+=cwid(' '); n++; }  

      else if (*c == 'O')                               /* O-slash */
        { strcat(s,"\\330"); w+=cwid('O'); n++; } 

      else if (*c == 'o')                               /* o-slash */
        { strcat(s,"\\370"); w+=cwid('O'); n++; } 
      
      else if((*c == 's')&&(*(c+1) == 's'))               /* sz */
        { c++; strcat(s,"\\337"); w+=cwid('s'); n++; } 
      else if((*c == 'a')&&(*(c+1) == 'a'))               /* a-ring */
        { c++; strcat(s,"\\345"); w+=cwid('a'); n++; } 
      else if((*c == 'A')&&(*(c+1) == 'A'))               /* A-ring */
        { c++; strcat(s,"\\305"); w+=cwid('A'); n++; }  
      else if((*c == 'a')&&(*(c+1) == 'e'))               /* ae */
        { c++; strcat(s,"\\346"); w+=1.5*cwid('a'); n++; }  
      else if((*c == 'A')&&(*(c+1) == 'E'))               /* AE */
        { c++; strcat(s,"\\306"); w+=1.5*cwid('A'); n++; }  


      else if (*c == 'c') {                           /* c-cedilla */ 
        c++;
        w+=cwid(*c);
        if      (*c == 'C') { strcat(s,"\\307"); n++; }
        else if (*c == 'c') { strcat(s,"\\347"); n++; }
        else              {sprintf(t,"%c",*c); strcat(s,t); n++; } 
      }

      else if (*c == '~') {                           /* n-twiddle */ 
        c++;
        w+=cwid(*c);
        if      (*c == 'N') { strcat(s,"\\321"); n++; }
        else if (*c == 'n') { strcat(s,"\\361"); n++; }
		/* 
		 *  cd: changed default behaviour in order to enable escaping of '~'
		 *  else              { sprintf(t,"%c",*c); strcat(s,t); n++; } 
		 */
		else              { sprintf(t,"~%c",*c); strcat(s,t); n++; } 
      }

      else                           /* \-something-else; pass through */
        {sprintf(t,"\\%c",*c); strcat(s,t); w+=cwid('A'); n++; }
    }
    
    else if (*c == '{')
      ;
    else if (*c == '}') 
      ;

    else                             /* other characters: pass though */
      { 
        sprintf(t,"%c",*c); 
        strcat(s,t); n++; 
        w+=cwid(*c);
      }
   
    c++;
  }
  *wid = w;

  return n;
}


/* ----- put_str: output a string in postscript ----- */
void put_str (str)
char *str;
{
  char s[801];
  float w;
  
  tex_str (str,s,&w);
  bput ("%s", s);
}

/* ----- set_font ----- */
void set_font (fp, font, add_bracket)
FILE *fp;
struct FONTSPEC font;
int add_bracket;
{
  int i,fnum;

  fnum = -1;
  for (i=0; i<nfontnames; i++) {
    if (!strcmp(font.name,fontnames[i])) fnum = i; 
  }
  if (fnum < 0) {
    printf ("\n+++ Font \"%s\" not predefined; using first in list\n",
            font.name); 
    fnum = 0;
  }
#if ABC_USE_GRAPHICS_LIB
  if (strstr (font.name, "Helvetica"))
    graphicsSetFont( gcx, kSansSerifFont, font.size );
  else
    graphicsSetFont( gcx, kSerifFont, font.size );
#else
  bput ("%.1f %d F%d ", font.size, font.box, fnum);
  if (add_bracket) bput ("(");
#endif
}

/* ----- set_font_str ----- */
void set_font_str (str,font)
char str[];
struct FONTSPEC font;
{
  int i,fnum;

  fnum=-1;
  for (i=0;i<nfontnames;i++) {
    if (!strcmp(font.name,fontnames[i])) fnum=i; 
  }
  sprintf (str,"%.1f %d F%d ", font.size, font.box, fnum);
}


/* ----- check_margin: do horizontal shift if needed ---- */
void check_margin (float new_posx)
{
  float dif;
  
  dif=new_posx-posx;
  if (dif*dif<0.001) return;

#if ABC_USE_GRAPHICS_LIB
  graphicsTranslate( gcx, 0, dif );
#else
  bput ("%.2f 0 T\n", dif);
#endif
  posx=new_posx;
}


/* ----- epsf_title ------ */
void epsf_title (title,fnm)
char title[],fnm[];
{
  char *p,*q;

  p=title; q=fnm;
  while (*p != '\0') {
    if (*p  ==  ' ') 
      *q = '_';
    else
      *q = *p;
    p++; q++;
  }
  *q = '\0';
}

/* ----- close_output_file ------ */
void close_output_file ()
{
#if !ABC_LIBRARY
  int m;
#endif

  if (!file_open) return;
  close_page(fout); 
#if !ABC_LIBRARY
  close_ps (fout);
  fclose (fout);
  if (tunenum == 0) 
    printf ("+++ Warning: no tunes written to output file\n");
  m=get_file_size (outfnam);
  printf ("Output written on %s (%d page%s, %d title%s, %d byte%s)\n", 
          outfnam, 
          pagenum, pagenum == 1 ? "" : "s", 
          tunenum, tunenum == 1 ? "" : "s",
          m, m == 1 ? "" : "s");
#endif
  file_open=0;
  file_initialized=0;
  
}


/* ----- open_output_file ------ */
void open_output_file (fnam,tstr)
char fnam[], tstr[];
{
#if !ABC_LIBRARY
#if !ABC_USE_GRAPHICS_LIB
  if (!strcmp(fnam,outfnam)) return;
  if (file_open) close_output_file ();
  strlcpy (outfnam, fnam, STRL1-1);
  if ((fout = fopen (outfnam,"w"))  ==  NULL) 
    rx ("Cannot open output file ", outf);
#endif
#endif
  pagenum = 0;
  tunenum = tnum1 = tnum2 = 0;
  file_open = 1;
  file_initialized = 0;
}

/* ----- open_index_file ------- */
void open_index_file (fnam)
char fnam[];
{
  if (vb>=8) printf("Open index file \"%s\"\n", fnam);
  if ((findex = fopen (fnam,"w"))  ==  NULL) 
    rx ("Cannot open index file: ", fnam);

  index_initialized=0;
  
}

/* ----- close_index_file ------- */
void close_index_file ()
{
  
  if (vb>=8) printf("Close index file\n");

  close_index_page (findex);

  fclose (findex);

}

/* ----- add_to_text_block ----- */
void add_to_text_block (ln,add_final_nl)
char ln[];
int add_final_nl;
{
  char *c,*a;
  char word[81];
  int nt,nl;

  nt=ntxt;
  c=ln;

  for (;;) {
    while (*c == ' ') c++;
    if (*c == '\0') break;
    a=word;
    while ((*c!=' ')&&(*c!='\0')&&(*c!='\n')) {
      nl=0;
      if ((*c == '\\')&&(*(c+1) == '\\')) {
        nl=1;
        c+=2;
        break;
      }
      *a=*c;
      c++;
      a++;

    }
    *a='\0';
    if (strlen(word)>MAXWLEN) {
      word[MAXWLEN-1]='\0';
      printf ("+++ Insanely long word truncated to %d chars: %s\n", 
              MAXWLEN-1,word);
    }
    if (nt>=MAXNTEXT) {
      printf ("\n+++ Line %d: %s\n", linenum,ln);
      rx("Text overflow; increase MAXNTEXT and recompile.","");
    }
    if (strlen(word)>0) {
      strlcpy (txt[nt], word, MAXWLEN-1);
      nt++;
    }
    if (nl) {
      strcpy (txt[nt], "$$NL$$");
      nt++;
    }
  }
  if (add_final_nl) {
    strcpy (txt[nt], "$$NL$$");
    nt++;
  }
  
  ntxt=nt;

}
  

/* ----- write_text_block ----- */
void write_text_block (fp,job)
FILE *fp;
int  job;
{
#if ABC_USE_GRAPHICS_LIB
  return;//FIXME
#endif
  int i,i1,i2,ntline,nc,mc,nbreak;
  float textwidth,ftline,ftline0,swfac,baseskip,parskip;
  float wwidth,wtot,spw;
  char str[81];

  if (ntxt<=0) return;

  baseskip = cfmt.textfont.size * cfmt.lineskipfac;
  parskip      = cfmt.textfont.size * cfmt.parskipfac;
  set_font_str (page_init,cfmt.textfont);

  /* estimate text widths.. ok for T-R, wild guess for other fonts */
  swfac=1.0;
  if (strstr(cfmt.textfont.name,"Times-Roman"))    swfac=1.00;
  if (strstr(cfmt.textfont.name,"Times-Bold"))     swfac=1.05;
  if (strstr(cfmt.textfont.name,"Helvetica"))      swfac=1.10;
  if (strstr(cfmt.textfont.name,"Helvetica-Bold")) swfac=1.15;
  if (strstr(cfmt.textfont.name,"Palatino"))       swfac=1.10;
  swfac=1.0;
  spw=cwid(' ');
  bput ("/LF {0 %.1f rmoveto} bind def\n",-baseskip);

  /* output by pieces, separate at newline token */
  i1=0;
  while (i1<ntxt) {
    i2=-1;
    for (i=i1;i<ntxt;i++) if(!strcmp(txt[i],"$$NL$$")) {i2=i; break; }
    if (i2 == -1) i2=ntxt;
    bskip(baseskip);

    if (job == OBEYLINES) {
      bput ("0 0 M (");
      for (i=i1;i<i2;i++) {
        tex_str(txt[i],str,&wwidth);
        bput ("%s ",str);
      }
      bput (") rshow\n");
    }

    else if (job == OBEYCENTER) {
      bput ("%.1f 0 M (",cfmt.staffwidth/2);
      for (i=i1;i<i2;i++) {
        tex_str(txt[i],str,&wwidth);
        bput ("%s",str);
        if (i<i2-1) bput (" ");
      }
      bput (") cshow\n");
    }
    
    else {
      bput ("0 0 M mark\n");
      nc=0;
      mc=-1;
      wtot=-spw;
      for (i=i2-1;i>=i1;i--) {
        mc+=tex_str(txt[i],str,&wwidth)+1;
        wtot+=wwidth+spw;
        nc+=strlen(str)+2;
        if (nc>=72) {nc=0; bput("\n"); }
        bput  ("(%s)",str);;
      }
      if (job == RAGGED)
        bput (" %.1f P1\n",cfmt.staffwidth);
      else 
        bput (" %.1f P2\n",cfmt.staffwidth);
          /* first estimate: (total textwidth)/(available width) */
          textwidth=wtot*swfac*cfmt.textfont.size;
      if (strstr(cfmt.textfont.name,"Courier")) 
        textwidth=0.60*mc*cfmt.textfont.size;
      ftline0=textwidth/cfmt.staffwidth;
      /* revised estimate: assume some chars lost at each line end */
      nbreak=ftline0;
      textwidth=textwidth+5*nbreak*cwid('a')*swfac*cfmt.textfont.size;
      ftline=textwidth/cfmt.staffwidth;
      ntline=ftline+1.0;
      if (vb>=10)
        printf("first estimate %.2f, revised %.2f\n", ftline0,ftline);
      if (vb>=10) 
        printf("Output %d word%s, about %.2f lines (fac %.2f)\n",
               i2-i1, i2-i1 == 1?"":"s", ftline,swfac);
      bskip((ntline-1)*baseskip);
    }

    buffer_eob (fp);  
    /* next line to allow pagebreak after each text "line" */
    /* if (!epsf && !within_tune) write_buffer(fp); */
    i1=i2+1;
  }
  bskip(parskip);
  buffer_eob (fp); 
  /* next line to allow pagebreak after each paragraph */
  if (!epsf && !within_tune) write_buffer(fp);
  strcpy (page_init,"");
  return;
}



/* ----- put_words ------- */
void put_words (fp)
FILE *fp;
{
  int i,nw,n;
  char str[81];
  char *p,*q;
  
  set_font (fp,cfmt.wordsfont, 0);
  set_font_str (page_init,cfmt.wordsfont);

  n=0;
  for (i=0;i<ntext;i++) if (text_type[i] == TEXT_W) n++;
  if (n == 0) return;
  
  bskip(cfmt.wordsspace);
  for (i=0;i<ntext;i++) {
    if (text_type[i] == TEXT_W) {
      bskip(cfmt.lineskipfac*cfmt.wordsfont.size);
      p=&text[i][0];
      q=&str[0];
      if (isdigit(text[i][0])) {
        while (*p != '\0') {
          *q=*p;
          q++;
          p++;
          if (*p == ' ') break;
          if (*(p-1) == ':') break;
          if (*(p-1) == '.') break;
        }
        if (*p == ' ') p++;
      }
      *q='\0';
      
      /* permit page break at empty lines or stanza start */
      nw=nwords(text[i]);
      if ((nw == 0) || (strlen(str)>0)) buffer_eob(fp);
      
      if (nw>0) {
        if (strlen(str)>0) {
#if ABC_USE_GRAPHICS_LIB
          cgrshow( gcx, 45, 0, str );
#else
          bput ("45 0 M (");
          put_str (str);
          bput (") lshow\n");
#endif
        }
        if (strlen(p)>0) {
#if ABC_USE_GRAPHICS_LIB
          cgrshow( gcx, 50, 0, p );
#else
          bput ("50 0 M (");
          put_str (p);
          bput (") rshow\n");
#endif
        }
      }
    }
  }

  buffer_eob (fp);
  strcpy (page_init,"");

}

/* ----- put_text ------- */
void put_text (fp, type, str)
FILE *fp;
int type;
char str[];
{
  int i,n;
  float baseskip,parskip;

  n=0;
  for (i=0;i<ntext;i++) if (text_type[i] == type) n++;
  if (n == 0) return;

  baseskip = cfmt.textfont.size * cfmt.lineskipfac;
  parskip  = cfmt.textfont.size * cfmt.parskipfac;
  bput ("0 0 M\n");
  ntxt=0;
  add_to_text_block(str,0);
  for (i=0;i<ntext;i++) {
    if (text_type[i] == type) add_to_text_block(text[i],1);
  }
  write_text_block (fp,RAGGED);
  buffer_eob (fp); 

}    

/* ----- put_history ------- */
void put_history (fp)
FILE *fp;
{
  int i,ok;
  float baseskip,parskip;

  set_font (fp, cfmt.textfont,0);
  set_font_str (page_init,cfmt.textfont);
  baseskip = cfmt.textfont.size * cfmt.lineskipfac;
  parskip  = cfmt.textfont.size * cfmt.parskipfac;

  bskip(cfmt.textspace);

  if (strlen(info.rhyth)>0) {
    bskip(baseskip); 
    bput ("0 0 M (Rhythm: ");
    put_str (info.rhyth);
    bput (") show\n");
    bskip(parskip);
  }
  
  if (strlen(info.book)>0) {
    bskip(0.5*CM); 
    bput ("0 0 M (Book: ");
    put_str (info.book);
    bput (") show\n");
    bskip(parskip);
  }

  if (strlen(info.src)>0) {
    bskip(0.5*CM); 
    bput ("0 0 M (Source: ");
    put_str (info.src);
    bput (") show\n");
    bskip(parskip);
  }

  put_text (fp, TEXT_D, "Discography: ");
  put_text (fp, TEXT_N, "Notes: ");
  put_text (fp, TEXT_Z, "Transcription: ");
  
  ok=0;
  for (i=0;i<ntext;i++) {
    if (text_type[i] == TEXT_H) {
      bskip(0.5*CM); 
      bput ("0 0 M (");
      put_str (text[i]);
      bput (") show\n");
      ok=1;
    }
  }
  if (ok) bskip(parskip);
  buffer_eob (fp);
  strcpy (page_init,"");

}


/* ----- write_inside_title  ----- */
void write_inside_title (fp)
FILE *fp;
{
  char t[201];

  if      (numtitle == 1) strlcpy (t, info.title, 200);
  else if (numtitle == 2) strlcpy (t, info.title2, 200);
  else if (numtitle == 3) strlcpy (t, info.title3, 200);
  if (vb>15) printf ("write inside title <%s>\n", t);
  if (strlen(t) == 0) return;

  bskip (cfmt.subtitlefont.size+0.2*CM);
  set_font (fp, cfmt.subtitlefont, 0);

  if (cfmt.titlecaps) cap_str(t);
#if ABC_USE_GRAPHICS_LIB
  if (cfmt.titleleft) cgrshow( gcx, 0, 0, t );
  else cgcshow( gcx, cfmt.staffwidth/2, 0, t );
#else
  bput (" (");
  put_str (t);
  if (cfmt.titleleft) bput (") 0 0 M rshow\n");
  else bput (") %.1f 0 M cshow\n", cfmt.staffwidth/2);
#endif
  bskip (cfmt.musicspace+0.2*CM);
}  


/* ----- write_tunetop ----- */
void write_tunetop(fp)
FILE *fp;
{
#if !ABC_USE_GRAPHICS_LIB
  bput ("\n\n%% --- tune %d %s\n", tunenum+1, info.title);
#endif
  if (!epsf) bskip (cfmt.topspace);
}


/* ----- write_tempo ----- */
void write_tempo(fp,tempo,meter)
FILE *fp;
char tempo[];
struct METERSTR meter;
{
  char *r, *q;
  char text[STRL];
  int top,bot,value,len,i,err,fac,dig,div;
  struct SYMBOL s;
  float stem,dotx,doty,sc,dx;

  if (vb>15) printf ("write tempo <%s>\n", info.tempo);
  r=tempo;
  set_font (fp,cfmt.tempofont,0);
#if ABC_USE_GRAPHICS_LIB
//  graphicsTranslate( gcx, 18, 0 );//FIXME
#else
  bput (" 18 0 M\n");
#endif

  for (;;) {

    while (*r == ' ') r++;                    /* skip blanks */
    if (*r == '\0') break;

    if (*r == '"') {                          /* write string */
      r++;
      q=text;
      while (*r!='"' && *r!='\0') { *q=*r; r++; q++; }
      if (*r == '"') r++;
      *q='\0';
      if (strlen(text)>0) {     
#if ABC_USE_GRAPHICS_LIB
        cgrshow( gcx, 6, 0, text );
#else
        bput ("6 0 rmoveto (");
        put_str (text);
        bput (") rshow 12 0 \n");
#endif
      }
    }

    else {                                  /* write tempo denotation */
      q=text;
      while (*r!=' ' && *r!='\0') { *q=*r; r++; q++; }
      *q='\0';
      
      q=text;
      len=QUARTER;
      value=0;
      err=0;
      if (strchr(q,'=')) {
        if (*q == 'C' || *q == 'c') {
          q++;
          len=meter.dlen;  
          div=0;
          if (*q == '/') { div=1; q++; }
          fac=0;
          while (isdigit(*q)) { dig=*q-'0'; fac=10*fac+dig; q++; }

          if (div) {
            if (fac == 0) fac=2;
            if (len%fac) printf ("Bad length divisor in tempo: %s", text);
            len=len/fac;
          }
          else  
            if (fac>0) len=len*fac;
          if (*q!='=') err=1;
          q++;
          if (!isdigit(*q)) err=1;
          sscanf(q,"%d", &value);
        }
        else if (isdigit(*q)) {
          sscanf(q,"%d/%d=%d", &top,&bot,&value);
          len=(BASE*top)/bot;
        }
        else err=1;
      } 
      else {
        if (isdigit(*q))
          sscanf(q,"%d", &value);
        else err=1;
      }
      
      if (err)                              /* draw note=value */
        printf("\n+++ invalid tempo specifier: %s\n", text);
      else {
        s.len=len;
        identify_note (&s,r);
        sc=0.55*cfmt.tempofont.size/10.0;
#if ABC_USE_GRAPHICS_LIB
        //FIXME
        graphicsSave( gcx );
        graphicsScale( gcx, sc, sc );
        if (s.head==H_OVAL)  cgHD( gcx, 15, 3 );
        if (s.head==H_EMPTY) cgHd( gcx, 15, 3 );
        if (s.head==H_FULL)  cghd( gcx, 15, 3 );
#else
        bput ("gsave %.2f %.2f scale 15 3 rmoveto currentpoint\n", sc, sc);
        if (s.head == H_OVAL)  bput ("HD");
        if (s.head == H_EMPTY) bput ("Hd");
        if (s.head == H_FULL)  bput ("hd");
#endif
        dx=4.0;
        if (s.dots) {      
          dotx=8; doty=0;
          if (s.flags>0) dotx=dotx+4;
          if (s.head == H_EMPTY) dotx=dotx+1;
          if (s.head == H_OVAL)  dotx=dotx+2;
          for (i=0;i<s.dots;i++) {
#if ABC_USE_GRAPHICS_LIB
            drawDot( gcx, 15, 3, dotx, doty );
#else
            bput (" %.1f %.1f dt", dotx, doty);
#endif
            dx=dotx;
            dotx=dotx+3.5;
          }
        }
        stem=16.0;
        if (s.flags>1) stem=stem+3*(s.flags-1);
#if ABC_USE_GRAPHICS_LIB
        if (s.len<WHOLE) drawStemUp( gcx, 15, 3, stem) ;
        if (s.flags>0) drawFlag( gcx, 15, 3, stem, s.flags, 'u' );
#else
        if (s.len<WHOLE) bput (" %.1f su",stem);
        if (s.flags>0) bput (" %.1f f%du",stem,s.flags);
#endif
        if ((s.flags>0) && (dx<6.0)) dx=6.0;
        dx=(dx+18)*sc;
#if ABC_USE_GRAPHICS_LIB
        graphicsRestore( gcx );
        char buf[128];
        sprintf( buf, " = %d", value );
        cgrshow( gcx, dx, 0, buf );
#else
        bput (" grestore %.2f 0 rmoveto ( = %d) rshow\n", dx,value);
#endif
      }
    }
  }
}


/* ----- write_inside_tempo  ----- */
void write_inside_tempo (fp)
FILE *fp;
{

  bskip (cfmt.partsfont.size);
  write_tempo(fp,info.tempo,voice[ivc].meter);
  bskip (0.1*CM);
}

/* ----- write_heading  ----- */
void write_heading (fp)
FILE *fp;
{
  float lwidth,down1,down2;
  int i,ncl;
  char t[201];

  lwidth=cfmt.staffwidth;

  /* write the main title */
  bskip (cfmt.titlefont.size+cfmt.titlespace);
#if ABC_USE_GRAPHICS_LIB
  set_font (fp,cfmt.titlefont,1);
//FIXME  if (cfmt.withxrefs) PUT1("%d. ", xrefnum)
  strcpy (t,info.title);
  if (cfmt.titlecaps) cap_str(t);
  if (cfmt.titleleft) graphicsDrawText( gcx, lwidth, 0, t, -1 );
  else graphicsDrawText( gcx, lwidth/2, 0, t, 0 );

  /* write second title */
  if (numtitle>=2) {
    bskip (cfmt.subtitlespace+cfmt.subtitlefont.size);
    set_font (fp,cfmt.subtitlefont,1);
    strcpy (t,info.title2);
    if (cfmt.titlecaps) cap_str(t);
    if (cfmt.titleleft) graphicsDrawText( gcx, lwidth, 0, t, -1 );
    else graphicsDrawText( gcx, lwidth/2, 0, t, 0 );
  }

  /* write third title */
  if (numtitle>=3) {
    bskip (cfmt.subtitlespace+cfmt.subtitlefont.size);
    set_font (fp,cfmt.subtitlefont,1);
    strcpy (t,info.title3);
    if (cfmt.titleleft) graphicsDrawText( gcx, lwidth, 0, t, -1 );
    else graphicsDrawText( gcx, lwidth/2, 0, t, 0 );
  }

  /* write composer, origin */
  if ((info.ncomp>0) || (strlen(info.orig)>0)) {
    set_font (fp,cfmt.composerfont,0);
    bskip(cfmt.composerspace);
    ncl=info.ncomp;
    if ((strlen(info.orig)>0) && (ncl<1)) ncl=1;
    for (i=0;i<ncl;i++) {
      bskip(cfmt.composerfont.size);
      graphicsDrawText( gcx, lwidth, 0, info.comp[i], kGraphicsAlignLeft );
//FIXME
//      if ((i == ncl-1)&&(strlen(info.orig)>0)) {
//        put_str (" (");
//        put_str (info.orig);
//        put_str (")");
//      }
    }
    down1=cfmt.composerspace+cfmt.musicspace+ncl*cfmt.composerfont.size;
  }
  else {
    bskip(cfmt.composerfont.size+cfmt.composerspace);
    down1=cfmt.composerspace+cfmt.musicspace+cfmt.composerfont.size;
  }
#else
  set_font (fp,cfmt.titlefont,1);
  if (cfmt.withxrefs) bput ("%d. ", xrefnum);
  strlcpy (t,info.title, 200);
  if (cfmt.titlecaps) cap_str(t);
  put_str (t);
  if (cfmt.titleleft) bput (") 0 0 M rshow\n");
  else bput (") %.1f 0 M cshow\n", lwidth/2);  

  /* write second title */
  if (numtitle>=2) {
    bskip (cfmt.subtitlespace+cfmt.subtitlefont.size);
    set_font (fp,cfmt.subtitlefont,1);
    strlcpy (t, info.title2, 200);
    if (cfmt.titlecaps) cap_str(t);
    put_str (t);
    if (cfmt.titleleft) bput (") 0 0 M rshow\n");
    else bput (") %.1f 0 M cshow\n", lwidth/2);
  }

  /* write third title */
  if (numtitle>=3) {
    bskip (cfmt.subtitlespace+cfmt.subtitlefont.size);
    set_font (fp,cfmt.subtitlefont,1);
    strlcpy (t, info.title3, 200);
    if (cfmt.titlecaps) cap_str(t);
    put_str (t);
    if (cfmt.titleleft) bput (") 0 0 M rshow\n");
    else bput (") %.1f 0 M cshow\n", lwidth/2);  
  }

  /* write composer, origin */
  if ((info.ncomp>0) || (strlen(info.orig)>0)) {
    set_font (fp,cfmt.composerfont,0);
    bskip(cfmt.composerspace);
    ncl=info.ncomp;
    if ((strlen(info.orig)>0) && (ncl<1)) ncl=1;
    for (i=0;i<ncl;i++) {
      bskip(cfmt.composerfont.size);
      PUT1("%.1f 0 M (", lwidth)
      put_str (info.comp[i]);
      if ((i==ncl-1)&&(strlen(info.orig)>0)) {
        put_str (" (");
        put_str (info.orig);
        put_str (")");
      }
      PUT0 (") lshow\n");
    }
    down1=cfmt.composerspace+cfmt.musicspace+ncl*cfmt.composerfont.size;
  }
  else {
    bskip(cfmt.composerfont.size+cfmt.composerspace);
    down1=cfmt.composerspace+cfmt.musicspace+cfmt.composerfont.size;
  }
#endif
  bskip (cfmt.musicspace);

  /* decide whether we need extra shift for parts and tempo */
  down2=cfmt.composerspace+cfmt.musicspace;
  if (strlen(info.parts)>0) down2=down2+cfmt.partsspace+cfmt.partsfont.size;
  if (strlen(info.tempo)>0) down2=down2+cfmt.partsspace+cfmt.partsfont.size;
  if (down2>down1) bskip (down2-down1);

  /* write tempo and parts */
  if (strlen(info.parts)>0 || strlen(info.tempo)>0) {
    
    if (strlen(info.tempo)>0) {
      bskip (-0.2*CM);
#if ABC_USE_GRAPHICS_LIB
      graphicsTranslate( gcx, cfmt.indent*cfmt.scale, 0 );
      write_tempo(fp, info.tempo, default_meter);
      graphicsTranslate( gcx, -cfmt.indent*cfmt.scale, 0 );
#else
      bput  (" %.2f 0 T ", cfmt.indent*cfmt.scale);
      write_tempo(fp, info.tempo, default_meter);
      bput  (" %.2f 0 T ", -cfmt.indent*cfmt.scale);
#endif
      bskip (-cfmt.tempofont.size);
    }
    
    if (strlen(info.parts)>0) {
      bskip (-cfmt.partsspace);
      set_font (fp,cfmt.partsfont,0);
#if ABC_USE_GRAPHICS_LIB
      graphicsDrawText( gcx, 0, 0, info.parts, kGraphicsAlignRight );
#else
      bput ("0 0 M (");
      put_str (info.parts);
      bput (") rshow\n");
#endif
      bskip (cfmt.partsspace);
    }

    if (strlen(info.tempo)>0) bskip (cfmt.tempofont.size+0.3*CM);
    
  }
  

}

/* ----- write_parts  ----- */
void write_parts (fp)
FILE *fp;
{
  if (strlen(info.parts)>0) {
    bskip (cfmt.partsfont.size);
    set_font (fp, cfmt.partsfont,0);
#if ABC_USE_GRAPHICS_LIB
    graphicsDrawText( gcx, 0, 0, info.parts, kGraphicsAlignRight );
#else
    bput ("0 0 M (");
    put_str (info.parts);
    bput (") rshow\n");
#endif
    bskip(cfmt.partsspace);
  }
}   

