
/*********************************************************************/
/*  bibView: Administration of BibTeX-Databases                      */
/*           (Verwaltung von BibTeX-Literaturdatenbanken)            */
/*                                                                   */
/*  Module:  globdata.c                                              */
/*                                                                   */
/*             Handling of global data                               */
/*             - List of bibdata                                     */
/*                                                                   */
/*  Author:  Holger Martin,  martinh@informatik.tu-muenchen.de       */
/*           Peter M. Urban, urban@informatik.tu-muenchen.de         */
/*                                                                   */
/*  History:                                                         */
/*    12.05.91  PMU  created                                         */
/*    05.26.92       Version 1.0 released                            */
/*                                                                   */
/*  Copyright 1992 TU MUENCHEN                                       */
/*    See ./Copyright for complete rights and liability information. */
/*                                                                   */
/*********************************************************************/

#include <stdio.h>
#include <ctype.h>
#include <unistd.h>
#include <X11/Intrinsic.h>
#include <X11/StringDefs.h>
#include <X11/Xaw/SimpleMenu.h>
#include <X11/Xaw/SmeBSB.h>
#include <X11/Xaw/SmeLine.h>
#include "bibview.h"


/* macros and definitions */
/* ---------------------- */

/* list type for opened bibliographies */
typedef struct bl {
   Bib bib;
   struct bl *prev,
	     *next;
} BibEl, *BibList;

/* CardEl and CardList are part of Bib in bibview.h */


/* local function prototypes */
/* ------------------------- */
static BibList FindBibListEl (BibPtr bp);
static CardList FindCardListEl (BibPtr bp, CardPtr cp);


/* external global variables */
/* ------------------------- */
extern char errorstr[];
extern char *errtitlestr;
extern char requiredfields[MAX_BIBTEX_TYPES+1][MAX_FIELDS];
extern char standardfields[MAX_BIBTEX_TYPES+1][MAX_FIELDS];
extern String cardNames[];
extern String fieldNames[];
extern int max_fields;
extern int max_bibtex_types;
extern char sort_field[];


/* exported global variables */
/* ---------------------- */


/* local global variables */
/* ------------------------- */


/* list of opened bibliographies */
static BibList bibList = NULL;

/*********************************************************************/
/*  Errcode Scalloc(String *s1, String s2):                          */
/*  Platz fuer s1 bereitstellen und s2 nach s1 kopieren              */
/*********************************************************************/

Errcode Scalloc(String *s1, String s2)
{
 if (s2 != NULL){
   *s1 = (String)XtCalloc(1,strlen(s2)+1);
   if (*s1 == NULL) return DBT_ECALLOC;
   strcpy(*s1, s2);
 }
 return DBT_OK;
}


/*********************************************************************/
/*  void CollapsePath(char *s1, char *s2):                           */
/*  Den Pfad in vereinfachen (. und .. entfernen)                    */
/*********************************************************************/

void 
CollapsePath(in, out)
     char *in, *out;
{
    char *p = in, *q = out, *pend = p + strlen(p);
    
    while (p < pend)
    {
	if (*p != '/')
	{
	    *q++ = *p++;
	}
	else if (p + 1 < pend && *(p + 1) == '/')
	{
	    ++p;
	}
	else if ( (p + 2 == pend && *(p + 1) == '.') || 
		  (p + 2 < pend && *(p + 1) == '.' && *(p + 2) == '/') )
	{
	    p += 2;
	}
	else if ( (p + 3 == pend && *(p + 1) == '.' && *(p + 2) == '.') ||
		 (p + 3 < pend && *(p + 1) == '.'
		                      && *(p + 2) == '.' && *(p + 3) == '/') )
	{
	    while (q > out && *--q != '/')
		;
	    p += 3;
	}
	else
	{
	    *q++ = *p++;
	}
    }
    if (q == out)
    {
	*q++ = '/';
    }

    while (q > out)
    {
	if (*--q != '/')
	    break;
    }
    *++q = '\0';
}

/*********************************************
*
*   Test, whether field ft belongs to type ct 
*
**********************************************/
int isstandardfield(int ft, int ct)
{ 
  int erg;
  erg = (standardfields[ct][ft] == '1');
  return(erg);
}


/*********************************************
*
*   Test, whether field ft is required by type ct 
*
**********************************************/
int isrequiredfield(int ft, int ct)
{ 
  int erg;
  erg = (requiredfields[ct][ft] == '1');
  return(erg);
}

/*********************************************************************/
/* strupr:                                                           */
/*    Convert characters 'a'..'z' to 'A'..'Z'                        */
/*********************************************************************/
char *
strupr (char *s)
{
char *p = s;

   while (*p) {
      if (*p >= 'a' && *p <= 'z')
	 *p = toupper(*p);
      p++;
   }

   return(s);
}


/*********************************************************************/
/* strlower:                                                         */
/*    Convert characters 'A'..'Z' to 'a'..'z'                        */
/*********************************************************************/
char *
strlower (char *s)
{
char *p = s;

   while (*p) {
      if (*p >= 'A' && *p <= 'Z')
	 *p = tolower(*p);
      p++;
   }

   return(s);
}


/*********************************************************************/
/* glbReadFileOpenBib:                                               */
/*    Read file and Open windows                                     */
/*********************************************************************/
void glbReadFileOpenBib(BibPtr bp)
{
Errcode status, bibstatus;

/* read file into tree and open bib window */
if ((status = bifFileRead(bp)) == BIF_EOPEN) {
   glbDelBibListEl(bp); 
   guwError(status);
   return;
}
if (status == ERR_NOBIB){
   guwError(status);
   glbDelBibListEl(bp); 
   return;
   }

if ((bibstatus = gubOpenBibWin(bp)) != OK) {
   guwError(bibstatus);
   return;
   }

if (status != BIF_OK) {
   if (cotDisplayErrWin()){
      status = gueOpenBibErrWin(bp, (int)status);   
      if (status != OK){
         guwError(SYNTAX_ERROR);
	 }
      else {
         hlpOpenHelpWin(7, errtitlestr, errorstr);
         guwError(ERR_ERROR_IN_FILE);
	 }
      }
   else
      guwError(SYNTAX_ERROR);
   }
else 
/* check cards, if option is on */
   if (cotAutoCheckBib()) 
      cseCheckBib(bp, False);      
}

/*********************************************************************/
/* glbNewString:                                                     */
/*    Copy an instance of a String                                   */
/*********************************************************************/
String
glbNewString (String s)
{
String str;

   if (s == NULL)
      return(NULL);

   if ((str = (char *)XtMalloc(strlen(s)+1)) == NULL)
      return(NULL);

   strcpy(str, s);
   return(str);
}


/*********************************************************************/
/* glbTrimString:                                                    */
/*    Remove whitespace before and after string                      */
/*********************************************************************/
String
glbTrimString (String s)
{
String p;

   while (*s == ' ' || *s == '\t') {
      p = s;
      while (*p) {
	 *p = *(p+1);
	 p++;
      }
   }

   p = s+strlen(s)-1;
   while (*p == ' ' || *p == '\t')
      *p-- = '\0';
   return(s);
}


/*********************************************************************/
/* glbIsStringEmpty:                                                 */
/*    Check for empty string                                         */
/*********************************************************************/
Boolean
glbIsStringEmpty (String s)
{
   if (s == NULL || *s == '\0') 
      return(TRUE);

   while (*s) {
      if (*s != ' ' || *s != '\t') 
          return(FALSE);
      s++;
   }

   return(TRUE);
}


/*********************************************************************/
/* glbTypeToName:                                                    */
/*    Return String name of card type                                */
/*********************************************************************/
String
glbTypeToName (int type)
{
   return(cardNames[type]);
}


/*********************************************************************/
/* glbFldToName:                                                     */
/*    Return name of field belonging to number of field              */
/*********************************************************************/
String
glbFldToName (int i)
{ 
   return(fieldNames[i]);
}


/*********************************************************************/
/* glbNameToType:                                                    */
/*    Return card type corresponding to name                         */
/*********************************************************************/
int
glbNameToType (String name)
{
int i = 0;

   for (i = 0; i < max_bibtex_types; i++) {
      if (strcmp(name, cardNames[i]) == 0)
         return(i);
   }
   return(-1);
}

/*********************************************************************/
/* glbNameToField:                                                   */
/*    Return field type corresponding to name                        */
/*********************************************************************/
int
glbNameToField (String name)
{
FieldName i = 0;
   
   for (i = 0; i < max_fields; i++) {
      if (strcmp(name, fieldNames[i]) == 0)
         return(i);
   }
   return(-1);
}


/*********************************************************************/
/* glbNewBibListEl:                                                  */
/*    Create new bliography list element                             */
/*********************************************************************/
Errcode
glbNewBibListEl (BibPtr *bp)
{
BibList bl, p;
Errcode status;
   /* malloc new list entry */
   if ((bl = (BibList)XtMalloc(sizeof(BibEl))) == NULL) {
      *bp = NULL;
      return(ERR_NOMALLOC);
   }


   /* initialize required data */
   if ((status = dbtGetFreeTreeIdx(&bl->bib.treeIdx)) != OK) {

      XtFree((char *)bl);
      return(status);
   }
   bl->bib.changed = FALSE;
   bl->bib.filename[0] = '\0';
   bl->bib.filepath[0] = '\0';
   bl->bib.tempfile = NULL;
   bl->bib.macrofile = NULL;
   bl->bib.bw = NULL;
   bl->bib.lw = NULL;
   bl->bib.mw = NULL;
   bl->bib.cl = NULL;
   bl->bib.noOfCardWins = 0;
   bl->bib.nextCardPos = 0;

   /* if list is empty, start new list */
   if (bibList == NULL) {
      bl->next = bl->prev = NULL;
      bibList = bl;
      *bp = &bl->bib;
      return(OK);
   }

   p = bibList;
   while (p->next != NULL)	/* find last element in list */
      p = p->next;

   bl->prev = p;		/* append new win to list */
   bl->next = NULL;
   p->next = bl;

   *bp = &bl->bib;
   return(OK);
}


/*********************************************************************/
/* glbDelBibListEl:                                                  */
/*    Delete bibliography list element                               */
/*********************************************************************/
Errcode
glbDelBibListEl (BibPtr bp)
{
BibList bn;	/* node of card to delete */

   /* check if card is really in list */
   if ((bn = FindBibListEl(bp)) == NULL)
      return(ERR_NOBIB);
   if (bp->tempfile != NULL)
      {unlink(bp->tempfile);
       XtFree((char *)bp->tempfile);
       }
   if (bp->macrofile != NULL)
      {unlink(bp->macrofile);
       XtFree((char *)bp->macrofile);
       }
   /* is it root of list? */
   if (bn == bibList) {
      bibList = bn->next;
      if (bn->next != NULL)
         bn->next->prev = NULL;
   }
   else {
      bn->prev->next = bn->next;
      if (bn->next != NULL)
         bn->next->prev = bn->prev;
   }
   XtFree((char *)bn); /* all other XtFrees must be done before */
   return(OK);
}


/*********************************************************************/
/* glbFirstBibListEl:                                                */
/*    Get element in root of list                                    */
/*********************************************************************/
BibPtr
glbFirstBibListEl (void)
{
   if (bibList != NULL)
      return(&bibList->bib);
   return(NULL);
}


/*********************************************************************/
/* glbNextBibListEl:                                                 */
/*    Get element after to bp in list of bibs                        */
/*********************************************************************/
BibPtr
glbNextBibListEl (BibPtr bp)
{
BibList bl;

   if ((bl = FindBibListEl(bp)) == NULL)
      return(NULL);
   return(&bl->next->bib);
}

/*********************************************************************/
/* glbIsBibListEl:                                                   */
/*    Test whether bib is really an element of bib list              */
/*********************************************************************/
Boolean
glbIsBibListEl (BibPtr bp)
{
   if (FindBibListEl(bp) != NULL)
      return(TRUE);
   return(FALSE);
}

/*********************************************************************/
/* glbCheckPath:                                                     */
/*    Test whether path is writeable  (important when saving files)  */
/*********************************************************************/
Errcode
glbCheckPath (String path, String name)
{
char tmpfile[MAX_FILEPATHLEN];
int j;
   
   strcpy(tmpfile, path);
   for (j=0; tmpfile[j] != '\0'; j++){
     if (tmpfile[j] == '/'){
       if (!strcmp(&tmpfile[j+1], name)){
	  tmpfile[j]='\0';
	  if (access(tmpfile, W_OK) != 0)
	     return(BIF_EWRITE);
          }
     }
   }
   return(OK);
}


/*********************************************************************/
/* glbMakeBackup:                                                    */
/*    Make Backup of file             (important when saving files)  */
/*********************************************************************/
Errcode
glbMakeBackup (String path)
{
int i;
char sysStr[2*MAX_FILEPATHLEN+20];
char tmpfile[MAX_FILEPATHLEN];

   i = 1;
   sprintf(tmpfile, "%s.bak.%d", path, i);
   while (access(tmpfile, F_OK) == 0) {
           i++;
           sprintf(tmpfile, "%s.bak.%d", path, i);
           }
   sprintf(sysStr, "mv -f %s %s.bak.%d", path, path, i);
   if (access(path, F_OK) == 0) {
      system(sysStr);
      return(OK);
      }
   else return(BIF_EWRITE);
}

/*********************************************************************/
/* FindBibListEl:                                                    */
/*    Find bib bp in list of bibs                                    */
/*********************************************************************/
static BibList
FindBibListEl (BibPtr bp)
{
BibList p;

   p = bibList;
   while (p != NULL) {
      if (&p->bib == bp)
	 return(p);
      p = p->next;
   }
   return(NULL);
}

/*********************************************************************/
/* glbDupCardListEl:                                                 */
/*    Create new bibliography card list element                      */
/*********************************************************************/
Errcode
glbDupCardListEl (BibPtr bp, CardPtr *cp, CardDataPtr cd, int type)
{
CardList cl, p;
Errcode status;

   /* malloc new list entry */
   if ((cl = (CardList)XtMalloc(sizeof(CardEl))) == NULL) {
      *cp = NULL;
      return(ERR_NOMALLOC);
   }

   /* initialize required data */
   cl->card.bp = bp;
   cl->card.cd = NULL;
   if ((status = DupCard(&cl->card.cd, cd, type)) != DBT_OK) {
      XtFree((char *)cl);
      return(status);
   }
   cl->card.cw = NULL;
   cl->card.changed = TRUE;
   cl->card.extended = FALSE;

   /* if list is empty, start new list */
   if (bp->cl == NULL) {
      cl->next = cl->prev = NULL;
      bp->cl = cl;
      *cp = &cl->card;
      return(OK);
   }

   p = bp->cl;
   while (p->next != NULL)	/* find last element in list */
      p = p->next;

   cl->prev = p;		/* append new win to list */
   cl->next = NULL;
   p->next = cl;

   *cp = &cl->card;
   return(OK);
}


/*********************************************************************/
/* glbNewCardListEl:                                                 */
/*    Create new bibliography card list element                      */
/*********************************************************************/
Errcode
glbNewCardListEl (BibPtr bp, CardPtr *cp, CardDataPtr cd)
{
CardList cl, p;
Errcode status;

   /* malloc new list entry */
   if ((cl = (CardList)XtMalloc(sizeof(CardEl))) == NULL) {
      *cp = NULL;
      return(ERR_NOMALLOC);
   }

   /* initialize required data */
   cl->card.bp = bp;
   cl->card.cd = NULL;
   if ((status = CopyCard(&cl->card.cd, cd)) != DBT_OK) {
      XtFree((char *)cl);
      return(status);
   }
   cl->card.cw = NULL;
   cl->card.changed = FALSE;
   cl->card.extended = FALSE;

   /* if list is empty, start new list */
   if (bp->cl == NULL) {
      cl->next = cl->prev = NULL;
      bp->cl = cl;
      *cp = &cl->card;
      return(OK);
   }

   p = bp->cl;
   while (p->next != NULL)	/* find last element in list */
      p = p->next;

   cl->prev = p;		/* append new win to list */
   cl->next = NULL;
   p->next = cl;

   *cp = &cl->card;
   return(OK);
}


/*********************************************************************/
/* glbDelCardListEl:                                                 */
/*    Delete bibliography card list element                          */
/*********************************************************************/
Errcode
glbDelCardListEl (BibPtr bp, CardPtr cp)
{
CardList cn;	/* node of card to delete */

   /* check if card is really in list */
   if ((cn = FindCardListEl(bp, cp)) == NULL)
      return(ERR_NOCARD);

   /* is it root of list? */
   if (cn == bp->cl) {
      bp->cl->prev = NULL;
      bp->cl = cn->next;
   }
   else {
      cn->prev->next = cn->next;
      if (cn->next != NULL)
         cn->next->prev = cn->prev;
   }
   XtFree((char *)cn); /* all other XtFrees must be done before */
   return(OK);
}

/*********************************************************************/
/* glbFirstCardListEl:                                               */
/*    Get element in root of list                                    */
/*********************************************************************/
CardPtr
glbFirstCardListEl (BibPtr bp)
{
   if (bp->cl != NULL)
      return(&bp->cl->card);
   return(NULL);
}

/*********************************************************************/
/* glbNextCardListEl:                                                */
/*    Get element after to cp in list of cards                       */
/*********************************************************************/
CardPtr
glbNextCardListEl (BibPtr bp, CardPtr cp)
{
CardList cl;

   if ((cl = FindCardListEl(bp, cp)) == NULL)
      return(NULL);
   return(&cl->next->card);
}


/*********************************************************************/
/* glbLastCardListEl:                                                */
/*    Get last element in list of cards                              */
/*********************************************************************/
CardPtr
glbLastCardListEl (BibPtr bp)
{
CardList cl = bp->cl;

   if (cl != NULL) {
      while (cl->next != NULL)
	 cl = cl->next;
      return(&cl->card);
   }
   else
      return(NULL);
}


/*********************************************************************/
/* glbIsCardListEl:                                                  */
/*    Test whether card is really an element of card list            */
/*********************************************************************/
Boolean
glbIsCardListEl (BibPtr bp, CardPtr cp)
{
   if (FindCardListEl(bp, cp) != NULL)
      return(TRUE);
   return(FALSE);
}


/*********************************************************************/
/* glbFindCard:                                                      */
/*    Search for card in list of opened card windows                 */
/*********************************************************************/
CardPtr
glbFindCard (BibPtr bp, CardDataPtr cd)
{
CardPtr cp = NULL;

   if (cd->mainkey == NULL)
      return(NULL);

   if ((cp = glbFirstCardListEl(bp)) == NULL)
      return(NULL);

   while (cp != NULL) {
      if (cp->cd->mainkey == NULL)
         cp = glbNextCardListEl(bp, cp);
      else if (strcmp(cd->mainkey, cp->cd->mainkey) == 0){
	 return(cp); 
	 }
      else
         cp = glbNextCardListEl(bp, cp);
   }
   return(NULL);
}

/***************************************************
 * Test whether file contains non ASCII characters *
 ***************************************************/
int glbContIllegalChar(char *dateiName)
{
 FILE *dptr;
 int inh;

 dptr = fopen(dateiName, "r");
 while ( ((inh = getc(dptr)) != EOF) && isascii(inh) ) ;
   
 fclose(dptr); 
 
 if (inh == EOF)
   return 1;
 else 
   return 0;
}

/*********************************************************************/
/* FindCardListEl:                                                   */
/*    Find card cp in list of cards of bp                            */
/*********************************************************************/
static CardList
FindCardListEl (BibPtr bp, CardPtr cp)
{
CardList p;

   p = bp->cl;
   while (p != NULL) {
      if (&p->card == cp)
	 return(p);
      p = p->next;
   }
   return(NULL);
}

/*********************************************************************/
/* glbCreateTypeMenu:                                                */
/*    Build pulldown menus with card types                           */
/*********************************************************************/
void
glbCreateTypeMenu (String menuName, Widget parent, Widget *menu, 
		   XtCallbackProc cb, XtPointer p)
{
static Widget w;
int i = 0;
char name[8];

   *menu = XtVaCreatePopupShell(menuName, 
             simpleMenuWidgetClass, parent, NULL);
   w = XtVaCreateManagedWidget("line1", smeLineObjectClass, *menu, NULL);
   for (i=0; i<max_bibtex_types; i++){
      sprintf(name, "item%d", i+1);
      w = XtVaCreateManagedWidget(name, smeBSBObjectClass, *menu,
	      XtNlabel, glbTypeToName(i), NULL);
      XtAddCallback(w, XtNcallback, cb, p);
   }
   w = XtVaCreateManagedWidget("line2", smeLineObjectClass, *menu, NULL);
   sprintf(name, "item%d", MAX_BIBTEX_TYPES+1);
   w = XtVaCreateManagedWidget(name, smeBSBObjectClass, *menu,
			 XtNlabel, glbTypeToName(MAX_BIBTEX_TYPES), NULL);
   XtAddCallback(w, XtNcallback, cb, p);
}


/*********************************************************************/
/* glbCreateFieldMenu:                                               */
/*    Build pulldown menus with field names                          */
/*********************************************************************/
void
glbCreateFieldMenu (String menuName, Widget parent, Widget *menu, 
		   XtCallbackProc cb, XtPointer p)
{
static Widget w;
int i = 0;
char name[8];

   *menu = XtVaCreatePopupShell(menuName, 
             simpleMenuWidgetClass, parent, NULL);
   w = XtVaCreateManagedWidget("line1", smeLineObjectClass, *menu, NULL);
   w = XtVaCreateManagedWidget("item1", smeBSBObjectClass, *menu,
	      XtNlabel, "mainkey", NULL);
   XtAddCallback(w, XtNcallback, cb, p);
   w = XtVaCreateManagedWidget("item2", smeBSBObjectClass, *menu,
	      XtNlabel, "BibTeX-Type", NULL);
   XtAddCallback(w, XtNcallback, cb, p);
   for (i=0; i<max_fields; i++){
      if (sort_field[i] != '0'){
         sprintf(name, "item%d", i+3);
         w = XtVaCreateManagedWidget(name, smeBSBObjectClass, *menu,
	      XtNlabel, glbFldToName(i), NULL);
         XtAddCallback(w, XtNcallback, cb, p);
      }
   }
}

