/*
* This file is part of jcabc2ps,
* Copyright (C) 1996,1997,1998  Michael Methfessel
* Copyright (C) 2000,2001,2002,2003 John Chambers
* See files jcabc2ps.c and GPL.txt for details.
*/

#include <sys/stat.h>
#include <ctype.h>
#include <string.h>

#include "jcabc2ps.h"
#include "util.h"

/*  low-level utilities  */

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* ----- error warning -----
*/
void wng (char msg[], char str[])
{
	fprintf(stderr,"+++ %s%s\n", msg, str);
}

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* ----- error exit -----
*/
void rx(msg, str)
char msg[],str[];
{
	fprintf(stderr,"\n+++ %s%s\n", msg, str);
	exit (1);
}

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*/
void rx1(char msg[], char c)
{
	fprintf(stderr,"\n+++ %s%c\n", msg, c);
	exit (1);
}

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
*/
void rxi(msg, i)
char msg[];
int i;
{
	fprintf(stderr,"\n+++ %s%d\n", msg, i);
	exit (1);
}

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* ----- bug: print message for internal error and maybe stop -----
*/
void bug(msg,fatal)
char msg[];
int fatal;
{
	fprintf(stderr,"\n\nThis cannot happen!");
	if (strlen(msg)>0) fprintf(stderr,"\nInternal error: %s.\n", msg);
	if (fatal) {
		fprintf(stderr,"Emergency stop.\n\n");
		exit(1);
	} else {
		fprintf(stderr,"Trying to continue...\n\n");
	}
}

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* ----- ranf(x1,x2): return random float between x1 and x2 ---
*/
float ranf(float x1, float x2)
{
	static int m=259200;   /* generator constants */
	static int a=421;
	static int c=54773;
	static int j=1;        /* seed */
	float r;

	j=(j*a+c)%m;
	r=x1+(x2-x1)*(double)j/(double)m;
	return r;
}

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* Added by jc:
* This routine reads a line from fp into buf,  and  trims  away  any  trailing
* whitespace. We are paranoid about whether isspace(c) returns true for CR, so
* this routine should  work  even  if  the  input  came  from  a  DOS  system.
* Additional paranoia:  We explicitly check for a newline, too, so this should
* work on Mac-like systems where a newline may not be a line terminator.
*
* 2009-12-31 change by jc:
* This was originally getline(), but that now conflicts with a definition  in
* POSIX 2008 and gets an error on recent linux releases. You'd think that our
* declaration would override the one in the system libraries, to prevent  the
* need for gratuitous changes like this, but I guess the implementers haven't
* seen it that way.
*/
char * getaline(buf,len,fp)
	char* buf;
	int   len;
	FILE* fp;
{	char* F="getaline";
	char* rp;
	int   c, l;
	if ((rp = fgets(buf,len,fp))) {
		l = strlen(buf);
		while ((l > 0) && (((c = buf[l-1]) && isspace(c)) || (c == '\r') || (c == '\n')))
			buf[--l] = 0;
	}
	V6 "%s: %s\n",F,rp V;
	return rp;
}

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* This is a routine to read a line into a Str struct, with expansion when  the
* line  is  too  long.  We also look for trailing backslashes, and when we see
* one, we read another line and append it.
*/
Str* getLine(lp,fp)
	Str  *lp;
	FILE *fp;
{	char *F="getLine";
	int   c, l, n, lines=0;
	char  buf[BSIZE+1];
	lp->l = 0;
	while (getaline(buf,BSIZE,fp)) {
		++lines;
		linenum++;	// Global line counter.
		l = strlen(buf);
		if ((n = lp->l + l) >= lp->m) {
			unless(minStr(lp,n)) {
				V1 "%s: ### Can't expand line from %d to %d chars ###\n",F,lp->l,n V;
				return 0;
			}
		}
		bcopy(buf,lp->v+lp->l,l);
		lp->v[lp->l = n] = 0;
		if (((lp->l = n) < 1) || (lp->v[lp->l-1] != '\\')) {	// Continued?
			V6 "%s: Got %d-char \"%s\"\n",F,lp->l,lp->v V;	// No; return it
			return lp;
		}
		lp->v[--lp->l] = 0;		// Wipe out the '\' char
		if ((lp->l > 0) && (lp->v[lp->l-1] == '\\')) {			// Ended with \\?
			V6 "%s: Return %d-char \"%s\"\n",F,lp->l,lp->v V;	// Yes; return it
			return lp;
		}
		// If we get here, the line was continued, so read another line
	}
	// If we get here, we hit EOF.
	if (lines) {	// Obscure case: continued last line
		V6 "%s: Return %d-char \"%s\"\n",F,lp->l,lp->v V;
		return lp;
	} else {		// Normal case: no more lines
		V6 "%s: Return EOF.\n",F V;
		return 0;
	}
}

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* ----- strip: remove leading and trailing blanks -----
*/
void strip(dst,src)
	char src[];
	char dst[];
{	int l, i, i1=0, i2=0;
	l=strlen(src);

	for (i=0; i<l; i++)
		if ((src[i]!=' ') && (src[i]!='\n')) { i1=i; break; }
	for (i=l-1; i>=0; i--)
		if ((src[i]!=' ') && (src[i]!='\n')) { i2=i+1; break; }
	for (i=i1;i<i2;i++) dst[i-i1]=src[i];
	dst[i2-i1]=0;
	V4 "strip: l=%d i1=%d i2=%d <%s> <%s>\n", l, i1, i2, src, dst V;
}

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* ----- nwords: count words in string -----
*/
int nwords (str)
char *str;
{
	int w,k;
	char *c;
	c=str;
	w=0;
	for(k=0;k<=50;k++) {
		while (*c==' ') c++;
		if (*c=='\0') break;
		w++;
		while ((*c!=' ') && (*c!='\0')) c++;
		if (*c=='\0') break;
	}
	return w;
}

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* ----- getword: return n-th word from string ----
*/
int getword (iw,str,str1)
int iw;
char *str,*str1;
{
	int w,k;
	char *c,*cc;
	if (iw<0) { *str1='\0'; return 0;}
	c=str;
	w=0;
	for(k=0;k<=50;k++) {
		while (*c==' ') c++;
		if (*c=='\0') break;
		if (w==iw) {
			cc=str1;
			while ((*c!=' ')&&(*c!='\0')) { *cc=*c; c++; cc++; }
			*cc='\0';
			return 1;
		}
		w++;
		while ((*c!=' ') && (*c!='\0')) c++;
		if (*c=='\0') break;
	}
	*str1='\0';
	return 0;
}

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* ----- abbrev: check for valid abbreviation -----
*/
int abbrev (str,ab,nchar)
char str[],ab[];
int nchar;
{
	int i,nc;
	if (strlen(str) > strlen(ab)) return 0;
	nc=strlen(str);
	if (nc<nchar) nc=nchar;
	for (i=0;i<nc;i++) if (str[i] != ab[i]) return 0;
	return 1;
}

/* * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
* ----- strext: set extension on a file identifier
*  force=1 forces change even if fid already has an extension
*  force=0 does not change the extension if there already is one
*/
void strext (fid1, fid, ext, force)
char fid[],ext[],fid1[];
int force;
{
	int i,l;
	char *p,*q;

	strcpy (fid1, fid);
	l=strlen(fid1);
	p=fid1;
	for (i=0;i<l;i++)
		if (fid1[i]=='/') p=fid1+i;

	if (!force) {
		q=strchr(p,'.');
		if (q && (q!=fid1+strlen(fid1)-1)) return;
	}
	if (!strchr(p,'.')) strcat (fid1,".");
	q=strchr(p,'.');
	if (strlen(ext)>0) q++;
	*q = 0;
	strcat(fid1,ext);

}

/* ----- cutext: cut off extension on a file identifier ----- */
void cutext (fid)
char fid[];
{
	int i,l;

	l=strlen(fid);
	for (i=0;i<l;i++)
		if (fid[i]=='.') fid[i]='\0';
	V6 "cutext: fid=\"%s\"\n",fid V;
}

/* ----- getext: get extension on a file identifier ----- */
void getext (fid,ext)
char fid[],ext[];
{
	int i,l,k;

	l=strlen(fid);
	k=l-1;
	for (i=0;i<l;i++)
		if (fid[i]=='.') k=i;

	for (i=k+1;i<l;i++)
		ext[i-k-1]=fid[i];
	ext[l-k-1]='\0';

}


/* ----- sscanu ----- */
float scan_u(str)
char str[];
{
	char unit[81];
	float a,b;

	strcpy(unit,"pt");
	sscanf(str,"%f%s", &a, unit);

	if      (!strcmp(unit,"cm")) b=a*CM;
	elsif (!strcmp(unit,"in")) b=a*IN;
	elsif (!strcmp(unit,"pt")) b=a*PT;
	else {
		fprintf(stderr,"+++ Unknown unit \"%s\" in: %s\n",unit,str);
		exit (3);
	}
	return b;
}


/* ----- match ------- */
int match (str, pat)
char str[], pat[];
{
	char *p,*s;
	p=pat;
	s=str;

	if (strlen(pat)==0) return 1;

	while (*p != 0) {

		if (*p == '*') {           /* found wildcard '*' in pattern */
			p++;
			while (*p == '*') p++;
			if (*p == 0) return 1;   /* trailing '*' matches all */
			for (;;) {               /* find match to char after '*' */
				if (*s == 0) return 0;
				if ((*s == *p) || (*p == '+'))
					if (match(s+1,p+1)) return 1;   /* ok if rest matches */
				s++;
			}
		}

		else {                     /* no wildcard -- char must match */
			if (*s == 0) return 0;
			if ((*p != *s) && (*p != '+')) return 0;
			s++;
		}
		p++;
	}

	if (*s != 0) return 0;       /* pattern but not string exhausted */
	return 1;
}

/* ----- isblankstr: check for blank string ---- */
int isblankstr (str)
char str[];
{
	int i;
	for (i=0;i<strlen(str);i++) if (str[i] != ' ') return 0;
	return 1;
}

/* ----- cap_str: capitalize a string ----- */
void cap_str(str)
char str[];
{
	char *c;
	for (c=str; *c!='\0'; c++)
	*c = toupper(*c);
}


/* ----- cwid ----- */
/*  These are char widths for Times-Roman */
float cwid(char c)
{
	float w;
	if    (c=='a') w=44.4;
	elsif (c=='b') w=50.0;
	elsif (c=='c') w=44.4;
	elsif (c=='d') w=50.0;
	elsif (c=='e') w=44.4;
	elsif (c=='f') w=33.3;
	elsif (c=='g') w=50.0;
	elsif (c=='h') w=50.0;
	elsif (c=='i') w=27.8;
	elsif (c=='j') w=27.8;
	elsif (c=='k') w=50.0;
	elsif (c=='l') w=27.8;
	elsif (c=='m') w=77.8;
	elsif (c=='n') w=50.0;
	elsif (c=='o') w=50.0;
	elsif (c=='p') w=50.0;
	elsif (c=='q') w=50.0;
	elsif (c=='r') w=33.3;
	elsif (c=='s') w=38.9;
	elsif (c=='t') w=27.8;
	elsif (c=='u') w=50.0;
	elsif (c=='v') w=50.0;
	elsif (c=='w') w=72.2;
	elsif (c=='x') w=50.0;
	elsif (c=='y') w=50.0;
	elsif (c=='z') w=44.4;

	elsif (c=='A') w=72.2;
	elsif (c=='B') w=66.7;
	elsif (c=='C') w=66.7;
	elsif (c=='D') w=72.2;
	elsif (c=='E') w=61.1;
	elsif (c=='F') w=55.6;
	elsif (c=='G') w=72.2;
	elsif (c=='H') w=72.2;
	elsif (c=='I') w=33.3;
	elsif (c=='J') w=38.9;
	elsif (c=='K') w=72.2;
	elsif (c=='L') w=61.1;
	elsif (c=='M') w=88.9;
	elsif (c=='N') w=72.2;
	elsif (c=='O') w=72.2;
	elsif (c=='P') w=55.6;
	elsif (c=='Q') w=72.2;
	elsif (c=='R') w=66.7;
	elsif (c=='S') w=55.6;
	elsif (c=='T') w=61.1;
	elsif (c=='U') w=72.2;
	elsif (c=='V') w=72.2;
	elsif (c=='W') w=94.4;
	elsif (c=='X') w=72.2;
	elsif (c=='Y') w=72.2;
	elsif (c=='Z') w=61.1;

	elsif (c=='0') w=50.0;
	elsif (c=='1') w=50.0;
	elsif (c=='2') w=50.0;
	elsif (c=='3') w=50.0;
	elsif (c=='4') w=50.0;
	elsif (c=='5') w=50.0;
	elsif (c=='6') w=50.0;
	elsif (c=='7') w=50.0;
	elsif (c=='8') w=50.0;
	elsif (c=='9') w=50.0;

	elsif (c=='~') w=54.1;
	elsif (c=='!') w=33.3;
	elsif (c=='@') w=92.1;
	elsif (c=='#') w=50.0;
	elsif (c=='$') w=50.0;
	elsif (c=='%') w=83.3;
	elsif (c=='^') w=46.9;
	elsif (c=='&') w=77.8;
	elsif (c=='*') w=50.0;
	elsif (c=='(') w=33.3;
	elsif (c==')') w=33.3;
/*|   elsif (c=='-') w=33.3; |*/
	elsif (c=='-') w=40.0;
	elsif (c=='_') w=50.0;
	elsif (c=='+') w=56.4;
	elsif (c=='=') w=55.0;
	elsif (c=='[') w=33.3;
	elsif (c==']') w=33.3;
	elsif (c=='{') w=48.0;
	elsif (c=='}') w=48.0;
	elsif (c=='|') w=20.0;
	elsif (c==':') w=27.8;
	elsif (c==';') w=27.8;
	elsif (c=='.') w=27.8;
	elsif (c==',') w=27.8;
	elsif (c=='\\') w=27.8;
	elsif (c=='\'') w=33.3;
	elsif (c=='\"') w=40.8;
	elsif (c=='<') w=56.4;
	elsif (c=='>') w=56.4;
	elsif (c=='?') w=44.4;
	elsif (c=='/') w=27.8;
	elsif (c=='`') w=33.3;
	elsif (c==' ') w=25.0;
	else             w=50.0;
	return w/100.0;
}


/* ----- get_file_size ------- */
/* version using standard function stat */
#include <sys/stat.h>
int get_file_size (fname)
	char fname[];
{
	int m,rc;
	struct stat statbuf;
	rc = stat(fname,&statbuf);
	if (rc == -1) {
		fprintf(stderr,"%s/%d: Unsuccessful call to stat for file \"%s\"\n",FL,fname);
		return -1;
	}
	m=statbuf.st_size;
	return m;
}

/* version which counts bytes by hand */
int get_file_size1 (fname)
char fname[];
{
	int m,i;
	FILE *fp;

	if ((fp = fopen (fname,"r")) == NULL) {
		fprintf(stderr,"Cannot open file to determine size: %s", fname);
		return -1;
	}

	m=0;
	i=getc(fp);
	while (i != EOF) {
		m++;
		i=getc(fp);
	}
	fclose (fp);
	return m;
}

