/*
CTAGS
This macro implements ME's CTAGS capability. It assumes that you have
generated a TAGS file using the CTAGS.EXE utility provided. Pressing the
<CTRL T> key will invoke this macro.

See the chapter on CTAGS in the ME user's manual for a description of how
this function works.
*/

#include mekeys.h

#define HORIZ     1
#define VERT      2
#define TOP_LEFT  3
#define TOP_RIGHT 4
#define BOT_LEFT  5
#define BOT_RIGHT 6

string BoxChars[10];


string tag_pattern;


init()
{
  assign_key("tags", CTRL_T);
}

tags()
{
  string funcname, fname;

  funcname = get_tty_str("What function? (Press <ENTER> for list) ");

  /*
    Get the name of the file which contains the chosen function
  */
  if ((fname = search_for_tag(funcname)) == "")
    return;
    
  /*
    Read the file
  */
  old_buf = currbuf();
  if (!(new_buf = find_buffer(fname)))
    new_buf = setcurrbuf(create_buffer(fname));

  /*
    Search the file for the fucntion definition
  */
  normalize_tagpat();
  gobof();
  if (fsearch(tag_pattern))
    show_buffer(new_buf);
  else
  {
    show_buffer(old_buf);
    get_tty_str("Sorry - can't find function %s", tag_pattern);
    bell();
  }
}


search_for_tag(funcname)
  string funcname;
{
  string tag_filename;

  /*
    Read the TAGS file into a buffer
  */
  old_buf = currbuf();
  tag_buf = create_buffer("TAGS");
  setcurrbuf(tag_buf);

  /* No TAGS file ? */
  if (currline() == "")
  {
    setcurrbuf(old_buf);
    delete_buffer(tag_buf);
    get_tty_str("There is no TAGS file");
    bell();
    return "";
  }
  
  tag_filename = "";

  /*
    The user wants to see a list of all functions.... Show it!
  */
  if (funcname == "")
  {
    funcname = show_all_tags();
    if (funcname == "")
    {
      setcurrbuf(old_buf);
      delete_buffer(tag_buf);
      return "";
    }
  }


  /*
    Search the tags file for the function which the user requested
  */
  if (fsearch(strcat("^", funcname)))
  {
    while (!is_eol() && currchar() != '\t')
      right();
    while (!is_eol() && currchar() == '\t')
      right();
    tag_filename = getword(0);
    tag_pattern  = getword(1);
  }
  else
  {
    bell();
    get_tty_str("Sorry - can't find function %s", funcname);
  }

  /*
    Get rid of the TAGS buffer
  */
  setcurrbuf(old_buf);
  delete_buffer(tag_buf);
  return tag_filename;
}


show_all_tags()
{
  string fname;

  tag_buf = currbuf();
  tmp_buf = create_buffer("FUNCTION LIST");
  setcurrbuf(tag_buf);

  while (!is_eof())
  {
    /*
      Extract the first word (the function name)
    */
    tabpos = index(line = currline(), "\t");
    if (tabpos > 0)
      fname = substr(line, 1, tabpos-1);
    else if ((tabpos = index(line = currline(), " ")) > 0)
      fname = substr(line, 1, tabpos-1);
    else
      fname = "<*** ERROR - NO TAB OR SPACE IN LINE ***>";

    setcurrbuf(tmp_buf);
    gobol();
    fast_insert(fname);  insert("\n");
    message("Processed [%s]", fname);
    setcurrbuf(tag_buf);
    if (!down())  break;
  }

  /*
    Get rid of the last blank line
  */
  setcurrbuf(tmp_buf);
  goeof();
  delline();
  gobof();

  fname = show_file(tmp_buf);
  goto xxx;


  /*
    Show the function list in a full-screen window
  */
  show_buffer(tmp_buf);
  explode_window();
  markline();   /* highlite the first entry */

  /*
    Let the user choose a function
  */
  while ((c = get_tty_char()) != ESC && c != '\r')
  {
    if (c == _UP)
      up_entry();
    else if (c == _DOWN)
      down_entry();
  }
  clear_mark();

  if (c == '\r')
    fname = currline();

  /*
    Get rid of the listing buffer
  */
  unexplode_window();

xxx:
  delete_buffer(tmp_buf);
  setcurrbuf(tag_buf);
  gobof();

  if (c == ESC)                 /* we didn't choose a file */
    return "";
  else                          /* we chose a file to edit */
    return fname;
}


up_entry()
{
  if (currlinenum() > 1)
  {
    clear_mark();
    up();
    markline();
  }
}

down_entry()
{
  if (currlinenum() < lastlinenum())
  {
    clear_mark();
    down();
    markline();
  }
}


/* normalize_tagpat - since '*' is a metachar, we must change a tag pattern */
/*   like "^char *foo()" to "^char ?foo()".  Kludgy, but it works!          */
normalize_tagpat()
{
  while ((i = index(tag_pattern, "*")) > 0)
  {
    tag_pattern = sprintf("%s?%s",
                 substr(tag_pattern, 1, i-1), substr(tag_pattern, i+1, 100));
  }
}

/*
A typical line of the TAGS file looks like this :
AllocPage[\t]VM.C[\t]/^PAGE *AllocPage()$/
*/
getword(eat_rest_of_line)
{
  /* Go past initial blanks */
  while (!is_eol() && (currchar() == '\t' || currchar() == ' '))
    right();
  if (currchar() == '?' || currchar() == '/')
    right();

  word = "";
  while (!is_eol() && (eat_rest_of_line || !isspace(currchar())))
  {
    word = strcat(word, chr(currchar()));
    right();
  }
  left();
  if (currchar() == '?' || currchar() == '/')
    word = substr(word, 1, strlen(word)-1);
  right();
  return word;
}



show_file(buf_id)
{
  int  filecolor;

  /* Get rid of the trailing newline in the function list */
  setcurrbuf(buf_id);
  goeof();
  delline();
  gobof();

  filecolor = 0x07;

  /* Create the menu and attach the function listing to it */
  win_id = create_window(8, 20, 15, 60, filecolor);
  attach_window(win_id, buf_id);
  DrawBox(6, 19, 15, 61, filecolor);
  display(15, 25, 52, filecolor, " Press UP, DN, ENTER or ESC ");

  /* Let the user interact with the menu */
  return MenuProcess();
}


/* ------------------------- MENU PROCESSING ------------------------- */

MenuProcess()
{
  markline();

  while ((c = get_tty_char()) != '\r' && c != ESC)
    if (c == _UP || c == _DOWN || c == PGUP || c == PGDN)
      MenuMove(c);
    else
      bell();
  
  if (c == '\r')
    return currline();
  else
    return "";
}

MenuMove(key)
{
  clear_mark();
  command(key);
  markline();
}

DrawBox(row1, col1, row2, col2, color)
{
  BoxChars[HORIZ]     = "Í";
  BoxChars[VERT]      = "º";
  BoxChars[TOP_LEFT]  = "É";
  BoxChars[TOP_RIGHT] = "»";
  BoxChars[BOT_LEFT]  = "È";
  BoxChars[BOT_RIGHT] = "¼";
  
  start_row = row1;
  start_col = col1;
  end_row   = row2;
  end_col   = col2;

  width = col2 - col1 + 1;
  horiz_line = repstr(BoxChars[HORIZ], width - 2);
  blank_line = repstr(" ", width - 2);

  display(row1, col1 + 1, col1 + width - 1,  color, horiz_line);
  display(row2, col1 + 1, col1 + width - 1,  color, horiz_line);
  for (r = row1+1;  r < row2;  r++)
  {
    display(r, col1, col1, color, BoxChars[VERT]);
    display(r, col2, col2, color, BoxChars[VERT]);
  }
  display(row1, col1, col1,  color, BoxChars[TOP_LEFT]);
  display(row1, col2, col2,  color, BoxChars[TOP_RIGHT]);
  display(row2, col1, col1,  color, BoxChars[BOT_LEFT]);
  display(row2, col2, col2,  color, BoxChars[BOT_RIGHT]);
}
