
/******************************************************************************
* MODULE     : file.gen.cc
* DESCRIPTION: file handling
* COPYRIGHT  : (C) 1999  Joris van der Hoeven
*******************************************************************************
* This software falls under the GNU general public license and comes WITHOUT
* ANY WARRANTY WHATSOEVER. See the file $TEXMACS_PATH/LICENSE for more details.
* If you don't have this file, write to the Free Software Foundation, Inc.,
* 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
******************************************************************************/

#include <file.gen.h>
#include <dir.gen.h>
#include <analyze.gen.h>
#include <hashmap.gen.h>

#module code_file
#import file
#import dir
#import analyze
#import hashmap (string, string)
#import hashmap (tree, string)
#include <iostream.h>

string var_eval_system (string s);

/*****************************************************************************/
// Decode file name
/*****************************************************************************/

string
decode_file_name (string s) {
  int i;
  string r(0);
  for (i=0; i<N(s); ) {
    if ((s[i]=='~') && ((i==0) || (s[i-1]==':'))) {
      r << get_env ("HOME");
      i++;
      continue;
    }
    if ((s[i]=='.') &&
	((i==0) || (s[i-1]==':')) &&
	((i==N(s)-1) || (s[i+1]==':')))
      {
	r << var_eval_system ("pwd");
	i++;
	continue;
      }
    if (s[i]=='$') {
      int start= ++i;
      while ((i<N(s)) && (s[i]!='/') && (s[i]!=':') &&
             (s[i]!='.') && (s[i]!='$')) i++;
      r << get_env (s (start, i));
      continue;
    }
    r << s[i++];
  }
  return r;
}

/*****************************************************************************/
// Constructors and destructors
/*****************************************************************************/

static string
un_point (string s) {
  int i;
  string r;
  for (i=0; i<N(s); )
    if ((i<(N(s)-2)) && (s[i]=='/') && (s[i+1]=='.') && (s[i+2]=='/')) i += 2;
    else if ((i<(N(s)-3)) && (s[i]=='/') &&
	     (s[i+1]=='.') && (s[i+2]=='.') && (s[i+3]=='/'))
      {
	int j;
	for (j=N(r)-1; j>=0; j--)
	  if (r[j]=='/') break;
	if (j>=0) r= r (0, j);
	i += 3;
      }
    else r << s[i++];
  return r;
}

static bool
get_from_web (string name) {
  if (is_web_file (name)) {
    string test= var_eval_system ("which wget");
    if ((N(test) < 4) || (test (N(test)-4, N(test)) != "wget")) return FALSE;
    string cmd= "wget --header='User-Agent: TeXmacs-1.0.0.1' -q -O $TEXMACS_HOME_PATH/system/from_web";
    system (cmd * " " * name);
    // system ("cat $TEXMACS_HOME_PATH/system/from_web");
    return (eval_system ("cat $TEXMACS_HOME_PATH/system/from_web") != "");
  }
  return FALSE;
}

file_rep::file_rep (string name2, char* rw_flag): dir (""), name (name2) {
  bool web_flag= (rw_flag[0]=='r') && get_from_web (name);
  if (web_flag) name= "$TEXMACS_HOME_PATH/system/from_web";

  string s= decode_file_name (name);
  char* _s= as_charp (s);
  fp= fopen (_s, rw_flag);
  delete[] _s;
  if (is_open ()) {
    if ((N(s)!=0) && (s[0]!='/'))
      s= var_eval_system ("pwd") * "/" * s;
    os_name= un_point (s);
  }

  if (web_flag) name= os_name= name2;
}

file_rep::file_rep (string dir2, string name2, char* rw_flag):
  dir (dir2), name (name2)
{
  bool web_flag= (rw_flag[0]=='r') && get_from_web (name);
  if (web_flag) {
    dir = "$TEXMACS_HOME_PATH/system";
    name= "from_web";
  }

  string Dir = decode_file_name (dir);
  string Name= decode_file_name (name);
  int i=0;

  do {
    int start= i;
    while ((i<N(Dir)) && (Dir[i]!=':')) i++;
    string s;
    string ss= Dir (start, i);
    if ((N(Name)>0) && (Name[0]=='/')) s= Name;
    else if (N(ss)==0) s= Name;
    else if ((N(ss)>0) && (ss[N(ss)-1]=='/')) s= ss * Name;
    else s= ss * "/" * Name;
    char* _s= as_charp (s);
    fp= fopen (_s, rw_flag);
    delete[] _s;
    if (is_open ()) {
      if ((N(s)!=0) && (s[0]!='/'))
	s= var_eval_system ("pwd") * "/" * s;
      os_name= un_point (s);
      if (is_directory (os_name)) { fclose (fp); fp= NULL; }
      else break;
    }
    if (i<N(Dir)) i++;
  } while (i<N(Dir));

  if (web_flag) name= os_name= name2;
}

file_rep::~file_rep () {
  if (fp!=NULL) fclose (fp);
}

bool
file_rep::is_open () {
  return (fp!=NULL);
}

void
file_rep::check_open (char* routine, char* in) {
  if (!is_open ()) {
    cerr << "\nFatal error: file " << name << " not found";
    if (routine[0]!='\0') cerr << " in " << routine;
    cerr << "\n";
    if (N(dir)>0) cerr << "           : directory was " << dir << "\n";
    if (in[0]!='\0') cerr << "See file   : " << in << "\n";
    exit (1);
  }
}

file::file (string name, char* rw_flag) {
  rep= new file_rep (name, rw_flag);
}

file::file (string dir, string name, char* rw_flag) {
  rep= new file_rep (dir, name, rw_flag);
}

/*****************************************************************************/
// Routines for manipulating files
/*****************************************************************************/

bool
eof (file f) {
  return feof (f->fp);
}

file&
operator >> (file& f, QI& c) {
  c= getc (f->fp);
  return f;
}

file&
operator >> (file& f, QN& c) {
  c= getc (f->fp);
  return f;
}

file&
operator >> (file& f, HI& c) {
  QI c1= getc (f->fp);
  QN c2= getc (f->fp);
  c= (((HI) c1)<<8)+ c2;
  return f;
}

file&
operator >> (file& f, HN& c) {
  QN c1= fgetc (f->fp);
  QN c2= fgetc (f->fp);
  c= (((HN) c1)<<8)+ c2;
  return f;
}

file&
operator >> (file& f, SI& c) {
  QI c1= getc (f->fp);
  QN c2= getc (f->fp);
  QN c3= getc (f->fp);
  QN c4= getc (f->fp);
  c= (((((((SI) c1)<<8)+ ((SI) c2))<<8)+ ((SI) c3))<<8)+ c4;
  return f;
}

file&
operator >> (file& f, string& s) {
  s= string (0);
  while (!feof (f->fp)) {
    char c= getc (f->fp);
    if ((c==((char) -1)) && (feof (f->fp))) break;
    s << c;
  }
  return f;
}

file&
operator << (file& f, string s) {
  int i;
  for (i=0; i<N(s); i++) fputc (s[i], f->fp);
  return f;
}

/*****************************************************************************/
// Loading or saving an entire file
/*****************************************************************************/

bool
load_string (string file_name, string& s, bool fatal) {
  file fin (file_name, "r");
  if (fatal) fin->check_open ("load_string", "file.gen.cc");
  else if (!fin->is_open()) return TRUE;
  fin >> s;
  return FALSE;
}

bool
load_string (string dir, string file_name, string& s, bool fatal) {
  file fin (dir, file_name, "r");
  if (fatal) fin->check_open ("load_string", "file.gen.cc");
  else if (!fin->is_open()) return TRUE;
  fin >> s;
  return FALSE;
}

bool
save_string (string file_name, string s, bool fatal) {
  file fout (file_name, "w");
  if (fatal) fout->check_open ("save_string", "file.gen.cc");
  else if (!fout->is_open()) return TRUE;
  fout << s;
  return FALSE;
}

bool
save_string (string dir, string file_name, string s, bool fatal) {
  file fout (dir, file_name, "w");
  if (fatal) fout->check_open ("save_string", "file.gen.cc");
  else if (!fout->is_open()) return TRUE;
  fout << s;
  return FALSE;
}

/******************************************************************************
* Loading and saving trees
******************************************************************************/

bool
load_tree (string file_name, tree& t, bool fatal) {
  string s;
  bool flag= load_string (file_name, s, fatal);
  t= texmacs_document_to_tree (s);
  return flag;
}

bool
load_tree (string dir, string file_name, tree& t, bool fatal) {
  string s;
  bool flag= load_string (dir, file_name, s, fatal);
  t= texmacs_document_to_tree (s);
  return flag;
}

bool
save_tree (string file_name, tree t, bool fatal) {
  return save_string (file_name, tree_to_texmacs_document (t), fatal);
}

bool
save_tree (string dir, string file_name, tree t, bool fatal) {
  return save_string (dir, file_name, tree_to_texmacs_document (t), fatal);
}

/******************************************************************************
* Loading xpm pixmaps
******************************************************************************/

tree
xpm_load (string file_name) {
  string s;
  load_string ("$TEXMACS_PIXMAPS_PATH", file_name, s);
  if (s == "") load_string ("$TEXMACS_PATH/misc/pixmaps/TeXmacs.xpm", s);

  int i, j;
  tree t (TUPLE);
  for (i=0; i<N(s); i++)
    if (s[i]=='\x22') {
      i++;
      j=i;
      while ((i<N(s)) && (s[i]!='\x22')) i++;
      t << s (j, i);
    }
  if (N(t)==0) return xpm_load ("$TEXMACS_PATH/misc/pixmaps/TeXmacs.xpm");
  return t;
}

static hashmap<string,string> xpm_size_table ("");

void
xpm_size (string file_name, int& w, int& h) {
  if (!xpm_size_table->contains (file_name)) {
    tree t= xpm_load (file_name);
    xpm_size_table (file_name)= t[0]->label;
  }
  
  int i= 0;
  bool ok;
  string s= xpm_size_table[file_name];
  skip_spaces (s, i);
  ok= read_int (s, i, w);
  skip_spaces (s, i);
  ok= read_int (s, i, h) && ok;
  if (!ok) fatal_error ("Invalid xpm (" * file_name * ")", "xpm_size");
}

/******************************************************************************
* Loading postscript files or conversion to postscript
******************************************************************************/

static string
load_postscript (string file_name) {
  string s;
  load_string (file_name, s);
  if (s == "")
    load_string ("$TEXMACS_PATH/misc/pixmaps/unknown.ps", s);
  return s;
}

static string
load_tiff (string file_name) {
  return eval_system ("tiff2ps " * file_name);
}

static string
load_pdf (string file_name) {
  string temp_name= get_temp_file_name ();
  system ("pdf2ps " * file_name * " " * temp_name);
  string s= load_postscript (temp_name);
  system ("rm -f " * temp_name);
  return s;
}

static string
load_pnm (string file_name) {
  return eval_system ("pnmtops -noturn " * file_name);
}

static string
load_png (string file_name) {
  string temp_name= get_temp_file_name ();
  system ("pngtopnm " * file_name * " > " * temp_name);
  string s= load_pnm (temp_name);
  system ("rm -f " * temp_name);
  return s;
}

static string
load_gif (string file_name) {
  string temp_name= get_temp_file_name ();
  system ("giftopnm " * file_name * " | cat > " * temp_name);
  string s= load_pnm (temp_name);
  system ("rm -f " * temp_name);
  return s;
}

static string
load_ppm (string file_name) {
  string temp_name= get_temp_file_name ();
  system ("ppmtogif " * file_name * " > " * temp_name);
  string s= load_gif (temp_name);
  system ("rm -f " * temp_name);
  return s;
}

static string
load_jpg (string file_name) {
  string temp_name= get_temp_file_name ();
  system ("djpeg -pnm " * file_name * " > " * temp_name);
  string s= load_pnm (temp_name);
  system ("rm -f " * temp_name);
  return s;
}

static string
load_xpm (string file_name) {
  string temp_name= get_temp_file_name ();
  system ("xpmtoppm " * file_name * " > " * temp_name);
  string s= load_ppm (temp_name);
  system ("rm -f " * temp_name);
  return s;
}

static string
load_fig (string file_name) {
  string temp_name= get_temp_file_name ();
  system ("fig2ps " * file_name * " " * temp_name);
  string s= load_postscript (temp_name);
  system ("rm -f " * temp_name);
  return s;
}

string
ps_load (string image, string type) {
  if (type == "ps") return image;
  if (type != "") {
    string temp_name= get_temp_file_name () * "." * type;
    save_string (temp_name, image);
    string s= ps_load (temp_name, "");
    system ("rm -f " * temp_name);
    return s;
  }

  int i;
  string radical;
  for (i=N(image); i>0; i--)
    if (image[i-1]=='.') break;
  if (i==0) return "";
  radical= image (i, N (image));

  if (get_from_web (image))
    image= "$TEXMACS_HOME_PATH/system/from_web";

  if (radical == "ps") return load_postscript (image);
  else if (radical == "eps") return load_postscript (image);
  else if (radical == "tif") return load_tiff (image);
  else if (radical == "pdf") return load_pdf (image);
  else if (radical == "pnm") return load_pnm (image);
  else if (radical == "png") return load_png (image);
  else if (radical == "gif") return load_gif (image);
  else if (radical == "ppm") return load_ppm (image);
  else if (radical == "jpg") return load_jpg (image);
  else if (radical == "xpm") return load_xpm (image);
  else if (radical == "fig") return load_fig (image);
  else return "";
}

hashmap<tree,string> ps_bbox ("");

void
ps_bounding_box (string image, string type,
		 int& x1, int& y1, int& x2, int& y2)
{
  tree lookup= tuple (image, type);
  if (!ps_bbox->contains (lookup)) {
    int i;
    string s= ps_load (image, type);
    string r= "0 0 32 32";
    for (i=0; i<N(s); i++)
      if (read (s, i, "%%BoundingBox: ")) {
	read_line (s, i, r);
	break;
      }
    ps_bbox (lookup)= r;
  }

  int i= 0;
  bool ok;
  string s= ps_bbox[lookup];
  skip_spaces (s, i);
  ok= read_int (s, i, x1);
  skip_spaces (s, i);
  ok= read_int (s, i, y1) && ok;
  skip_spaces (s, i);
  ok= read_int (s, i, x2) && ok;
  skip_spaces (s, i);
  ok= read_int (s, i, y2) && ok;
  if (ok) return;
  x1= y1= 0; x2= 596; y2= 842;
}

/******************************************************************************
* Other useful functions for files
******************************************************************************/

bool
is_web_file (string name) {
  return starts (name, "http:") || starts (name, "ftp:");
}

bool
file_exists (string file_name) {
  file fin (file_name, "r");
  return fin->is_open();
}

bool
file_exists (string dir, string file_name) {
  file fin (dir, file_name, "r");
  return fin->is_open();
}

string
get_unique_file_name (string dir, string file_name, bool fatal) {
  file fin (dir, file_name, "r");
  if (fatal) fin->check_open ("get_unique_file_name", "file.gen.cc");
  else if (!fin->is_open ()) return file_name;
  return fin->os_name;
}

string
get_temp_file_name () {
  char buffer [L_tmpnam+1];
  if (tmpnam (buffer) == NULL)
    fatal_error ("Couldn't create a temporary file name",
		 "get_temp_file_name", "file.gen.cc");
  return string (buffer);
}

string
get_radical_file_name (string base) {
  int i;
  for (i=N(base)-1; i>=0; i--)
    if (base[i]=='/') {
      while ((i>0) && (base[i-1]=='/')) i--;
      return base (0, i);
    }
  return ".";
}

string
get_tail_file_name (string base) {
  int i;
  for (i=N(base)-1; i>=0; i--)
    if (base[i]=='/') {
      while ((i>0) && (base[i-1]=='/')) i--;
      return base (i+1, N(base));
    }
  return base;
}

string
get_relative_file_name (string base, string name, bool fatal) {
  if ((N(name)>0) && ((name[0]=='/') || (name[0]=='$') || (name[0]=='~')))
    return get_unique_file_name (".", name, fatal);
  if (is_web_file (name)) return name;
  if (is_web_file (base)) {
    string r= get_radical_file_name (base);
    if ((r == "http:") || (r == "ftp:")) r= base;
    return r * "/" * name;
  }
  string r= get_radical_file_name (base) * "/" * name;
  return get_unique_file_name (".", r, fatal);
}

string
get_delta_file_name (string base, string name) {
  int i, j=0, k=0;
  for (i=0; (i<N(base)) && (i<N(name)); i++) {
    if (base[i] != name[i]) break;
    if (base[i] == '/') j=i+1;
  }
  for (i=j; i<N(base); i++)
    if (base[i] == '/') k++;
  string r= name (j, N(name));
  for (i=0; i<k; i++) r= "../" * r;
  return r;
}

string
get_file_type (string file_name) {
  int i;
  for (i=N(file_name)-1; i>=0; i--)
    if (file_name[i]=='.') break;
  if ((i>0) && (i<N(file_name)-1)) {
    string r= file_name (i+1, N(file_name));
    while ((N(r)>0) && (r[N(r)-1]=='~')) r= r(0, N(r)-1);
    return r;
  }
  return "";
}

string
get_file_format (string file_name) {
  string tp= get_file_type (file_name);
  if ((tp == "tm") || (tp == "ts") || (tp == "tp")) return "TeXmacs";
  if ((tp == "tex") || (tp == "sty") || (tp == "cls")) return "latex";
  if (tp == "html") return "html";
  else return "verbatim";
}

bool
has_no_name (string s) {
  return (N(s)>=7) && (s(0,7)=="no name");
}

/******************************************************************************
* System functions
******************************************************************************/

int
system (string s) {
  char* _s= as_charp (s);
  int r= system (_s);
  delete[] _s;
  return r;
}

string
eval_system (string s) {
  string temp= get_temp_file_name ();
  system (s * " > " * temp);
  string result;
  bool flag= load_string (temp, result, FALSE);
  system ("rm " * temp);
  if (flag) return "";
  return result;
}

string
var_eval_system (string s) {
  string r= eval_system (s);
  while ((N(r)>0) && (r[N(r)-1]=='\n')) r= r (0,N(r)-1);
  return r;
}

string
get_env (string var) {
  char* _var= as_charp (var);
  char* _ret= getenv (_var);
  delete[] _var;
  if (_ret==NULL) return "";
  string ret (_ret);
  return ret;
  // do not delete _ret !
}

void
set_env (string var, string with) {
#os gnu-linux
  char* _var = as_charp (var);
  char* _with= as_charp (with);
  setenv (_var, _with, 1);
#os powerpc-gnu-linux
  char* _var = as_charp (var);
  char* _with= as_charp (with);
  setenv (_var, _with, 1);
#os sun
  char* _varw= as_charp (var * "=" * with);
  (void) putenv (_varw);
#os dec
  char* _varw= as_charp (var * "=" * with);
  (void) putenv (_varw);
#os s390
  char* _varw= as_charp (var * "=" * with);
  (void) putenv (_varw);
#os cygwin
  char* _var = as_charp (var);
  char* _with= as_charp (with);
  setenv (_var, _with, 1);
#os unknown
  char* _var = as_charp (var);
  char* _with= as_charp (with);
  setenv (_var, _with, 1);
#os all
  // do not delete _var and _with !!!
}

static tree
analyze (string s, char c) {
  tree t (TUPLE);
  int i=0, last= 0, n= N(s);
  while ((i<n) && (s[i]==c)) i++;
  for (; i<n; i++)
    if (s[i] == c) {
      t << s (last, i);
      while ((i<n) && (s[i]==c)) i++;
      last= i; i--;
    }
  if ((n>0) && (s[n-1]!=c))
    t << s (last, n);
  return t;
}

static int
search_pos (tree t, string what) {
  int i, n= N(t);
  for (i=0; i<n; i++)
    if (t[i] == what)
      return i;
  return -1;
}

static bool
check_pos (tree t, int pos, string what) {
  int i, n= N(t);
  for (i=0; i<n; i++)
    if ((N(t[i])>pos) && (t[i][pos] == what))
      return TRUE;
  return FALSE;
}

void
recursive_kill (int pid) {
  string s= eval_system ("ps -l");
  int i, n= N(s);
  for (i=0; i<n; i++)
    if (s[i] == '\t')
      s[i]= ' ';
  tree t= analyze (s, '\n');
  n= N(t);
  for (i=0; i<n; i++)
    t[i]= analyze (t[i]->label, ' ');

  if (n>1) {
    int pid_pos = search_pos (t[0], "PID");
    int ppid_pos= search_pos (t[0], "PPID");
    if (pid_pos  == -1) pid_pos = search_pos (t[0], "pid");
    if (ppid_pos == -1) ppid_pos= search_pos (t[0], "ppid");
    if (pid_pos  == -1) pid_pos = search_pos (t[0], "Pid");
    if (ppid_pos == -1) ppid_pos= search_pos (t[0], "Ppid");
    if (ppid_pos == -1) ppid_pos= search_pos (t[0], "PPid");
    if (pid_pos  == -1) pid_pos = 3;
    if (ppid_pos == -1) ppid_pos= 4;
    if (check_pos (t, pid_pos, as_string (pid)) &&
	check_pos (t, ppid_pos, as_string (pid)))
      {
	for (i=0; i<n; i++)
	  if (t[i][ppid_pos] == as_string (pid))
	    recursive_kill (as_int (t[i][pid_pos]));
      }
  }
  // cout << "Killing " << pid << "\n";
  system ("kill -9 " * as_string (pid) * " 2> /dev/null");
}

bool (*guile_eval_routine) (string s) = NULL;
int script_status = 1;

bool
guile_eval (string s) {
  if (guile_eval_routine == NULL) return TRUE;
  else return guile_eval_routine (s);
}

#endmodule // code_file
