misc.c   [plain text]


/* Copyright (c) 1993-2002
 *      Juergen Weigert (jnweiger@immd4.informatik.uni-erlangen.de)
 *      Michael Schroeder (mlschroe@immd4.informatik.uni-erlangen.de)
 * Copyright (c) 1987 Oliver Laumann
 *
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2, or (at your option)
 * any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program (see the file COPYING); if not, write to the
 * Free Software Foundation, Inc.,
 * 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA
 *
 ****************************************************************
 */

#include "rcs.h"
RCS_ID("$Id: misc.c,v 1.1.1.2 2003/03/19 21:16:18 landonf Exp $ FAU")

#include <sys/types.h>
#include <sys/stat.h>	/* mkdir() declaration */
#include <signal.h>

#include "config.h"
#include "screen.h"
#include "extern.h"

#ifdef SVR4
# include <sys/resource.h>
#endif

extern struct layer *flayer;

extern int eff_uid, real_uid;
extern int eff_gid, real_gid;
extern struct mline mline_old;
extern struct mchar mchar_blank;
extern char *null, *blank;

char *
SaveStr(str)
register const char *str;
{
  register char *cp;

  if ((cp = malloc(strlen(str) + 1)) == NULL)
    Panic(0, strnomem);
  else
    strcpy(cp, str);
  return cp;
}

/* cheap strstr replacement */
char *
InStr(str, pat)
char *str;
const char *pat;
{
  int npat = strlen(pat);
  for (;*str; str++)
    if (!strncmp(str, pat, npat))
      return str;
  return 0;
}

#ifndef HAVE_STRERROR
char *
strerror(err)
int err;
{
  extern int sys_nerr;
  extern char *sys_errlist[];

  static char er[20];
  if (err > 0 && err < sys_nerr)
    return sys_errlist[err];
  sprintf(er, "Error %d", err);
  return er;
}
#endif

void
centerline(str, y)
char *str;
int y;
{
  int l, n;

  ASSERT(flayer);
  n = strlen(str);
  if (n > flayer->l_width - 1)
    n = flayer->l_width - 1;
  l = (flayer->l_width - 1 - n) / 2;
  LPutStr(flayer, str, n, &mchar_blank, l, y);
}

void
leftline(str, y)
char *str;
int y;
{
  int l, n;
  struct mchar mchar_dol;

  mchar_dol = mchar_blank;
  mchar_dol.image = '$';

  ASSERT(flayer);
  l = n = strlen(str);
  if (n > flayer->l_width - 1)
    n = flayer->l_width - 1;
  LPutStr(flayer, str, n, &mchar_blank, 0, y);
  if (n != l)
    LPutChar(flayer, &mchar_dol, n, y);
}


char *
Filename(s)
char *s;
{
  register char *p = s;

  if (p)
    while (*p)
      if (*p++ == '/')
        s = p;
  return s;
}

char *
stripdev(nam)
char *nam;
{
#ifdef apollo
  char *p;
  
  if (nam == NULL)
    return NULL;
# ifdef SVR4
  /* unixware has /dev/pts012 as synonym for /dev/pts/12 */
  if (!strncmp(nam, "/dev/pts", 8) && nam[8] >= '0' && nam[8] <= '9')
    {
      static char b[13];
      sprintf(b, "pts/%d", atoi(nam + 8));
      return b;
    }
# endif /* SVR4 */
  if (p = strstr(nam,"/dev/"))
    return p + 5;
#else /* apollo */
  if (nam == NULL)
    return NULL;
  if (strncmp(nam, "/dev/", 5) == 0)
    return nam + 5;
#endif /* apollo */
  return nam;
}


/*
 *    Signal handling
 */

#ifdef POSIX
sigret_t (*xsignal(sig, func))
# ifndef __APPLE__
 __P(SIGPROTOARG)
# else
()
# endif
int sig;
sigret_t (*func) __P(SIGPROTOARG);
{
  struct sigaction osa, sa;
  sa.sa_handler = func;
  (void)sigemptyset(&sa.sa_mask);
#ifdef SA_RESTART
  sa.sa_flags = (sig == SIGCHLD ? SA_RESTART : 0);
#else
  sa.sa_flags = 0;
#endif
  if (sigaction(sig, &sa, &osa))
    return (sigret_t (*)__P(SIGPROTOARG))-1;
  return osa.sa_handler;
}

#else
# ifdef hpux
/*
 * hpux has berkeley signal semantics if we use sigvector,
 * but not, if we use signal, so we define our own signal() routine.
 */
void (*xsignal(sig, func)) __P(SIGPROTOARG)
int sig;
void (*func) __P(SIGPROTOARG);
{
  struct sigvec osv, sv;

  sv.sv_handler = func;
  sv.sv_mask = sigmask(sig);
  sv.sv_flags = SV_BSDSIG;
  if (sigvector(sig, &sv, &osv) < 0)
    return (void (*)__P(SIGPROTOARG))(BADSIG);
  return osv.sv_handler;
}
# endif	/* hpux */
#endif	/* POSIX */


/*
 *    uid/gid handling
 */

#ifdef HAVE_SETEUID

void
xseteuid(euid)
int euid;
{
  if (seteuid(euid) == 0)
    return;
  seteuid(0);
  if (seteuid(euid))
    Panic(errno, "seteuid");
}

void
xsetegid(egid)
int egid;
{
  if (setegid(egid))
    Panic(errno, "setegid");
}

#else /* HAVE_SETEUID */
# ifdef HAVE_SETREUID

void
xseteuid(euid)
int euid;
{
  int oeuid;

  oeuid = geteuid();
  if (oeuid == euid)
    return;
  if (getuid() != euid)
    oeuid = getuid();
  if (setreuid(oeuid, euid))
    Panic(errno, "setreuid");
}

void
xsetegid(egid)
int egid;
{
  int oegid;

  oegid = getegid();
  if (oegid == egid)
    return;
  if (getgid() != egid)
    oegid = getgid();
  if (setregid(oegid, egid))
    Panic(errno, "setregid");
}

# endif /* HAVE_SETREUID */
#endif /* HAVE_SETEUID */



#ifdef NEED_OWN_BCOPY
void
xbcopy(s1, s2, len)
register char *s1, *s2;
register int len;
{
  if (s1 < s2 && s2 < s1 + len)
    {
      s1 += len;
      s2 += len;
      while (len-- > 0)
	*--s2 = *--s1;
    }
  else
    while (len-- > 0)
      *s2++ = *s1++;
}
#endif	/* NEED_OWN_BCOPY */

void
bclear(p, n)
char *p;
int n;
{
  bcopy(blank, p, n);
}


void
Kill(pid, sig)
int pid, sig;
{
  if (pid < 2)
    return;
  (void) kill(pid, sig);
}

void
closeallfiles(except)
int except;
{
  int f;
#ifdef SVR4
  struct rlimit rl;
  
  if ((getrlimit(RLIMIT_NOFILE, &rl) == 0) && rl.rlim_max != RLIM_INFINITY)
    f = rl.rlim_max;
  else
#endif /* SVR4 */
#if defined(SYSV) && defined(NOFILE) && !defined(ISC)
  f = NOFILE;
#else /* SYSV && !ISC */
  f = getdtablesize();
#endif /* SYSV && !ISC */
  while (--f > 2)
    if (f != except)
      close(f);
}



/*
 *  Security - switch to real uid
 */

#ifndef USE_SETEUID
static int UserPID;
static sigret_t (*Usersigcld)__P(SIGPROTOARG);
#endif
static int UserSTAT;

int
UserContext()
{
#ifndef USE_SETEUID
  if (eff_uid == real_uid && eff_gid == real_gid)
    return 1;
  Usersigcld = signal(SIGCHLD, SIG_DFL);
  debug("UserContext: forking.\n");
  switch (UserPID = fork())
    {
    case -1:
      Msg(errno, "fork");
      return -1;
    case 0:
      signal(SIGHUP, SIG_DFL);
      signal(SIGINT, SIG_IGN);
      signal(SIGQUIT, SIG_DFL);
      signal(SIGTERM, SIG_DFL);
# ifdef BSDJOBS
      signal(SIGTTIN, SIG_DFL);
      signal(SIGTTOU, SIG_DFL);
# endif
      setuid(real_uid);
      setgid(real_gid);
      return 1;
    default:
      return 0;
    }
#else
  xseteuid(real_uid);
  xsetegid(real_gid);
  return 1;
#endif
}

void
UserReturn(val)
int val;
{
#ifndef USE_SETEUID
  if (eff_uid == real_uid && eff_gid == real_gid)
    UserSTAT = val;
  else
    _exit(val);
#else
  xseteuid(eff_uid);
  xsetegid(eff_gid);
  UserSTAT = val;
#endif
}

int
UserStatus()
{
#ifndef USE_SETEUID
  int i;
# ifdef BSDWAIT
  union wait wstat;
# else
  int wstat;
# endif

  if (eff_uid == real_uid && eff_gid == real_gid)
    return UserSTAT;
  if (UserPID < 0)
    return -1;
  while ((errno = 0, i = wait(&wstat)) != UserPID)
    if (i < 0 && errno != EINTR)
      break;
  (void) signal(SIGCHLD, Usersigcld);
  if (i == -1)
    return -1;
  return WEXITSTATUS(wstat);
#else
  return UserSTAT;
#endif
}

#ifndef HAVE_RENAME
int
rename (old, new)
char *old;
char *new;
{
  if (link(old, new) < 0)
    return -1;
  return unlink(old);
}
#endif


int
AddXChar(buf, ch)
char *buf;
int ch;
{
  char *p = buf;

  if (ch < ' ' || ch == 0x7f)
    {
      *p++ = '^';
      *p++ = ch ^ 0x40;
    }
  else if (ch >= 0x80)
    {
      *p++ = '\\';
      *p++ = (ch >> 6 & 7) + '0';
      *p++ = (ch >> 3 & 7) + '0';
      *p++ = (ch >> 0 & 7) + '0';
    }
  else
    *p++ = ch;
  return p - buf;
}

int
AddXChars(buf, len, str)
char *buf, *str;
int len;
{
  char *p;

  if (str == 0)
    {
      *buf = 0;
      return 0;
    }
  len -= 4;     /* longest sequence produced by AddXChar() */
  for (p = buf; p < buf + len && *str; str++)
    {
      if (*str == ' ')
        *p++ = *str;
      else
        p += AddXChar(p, *str);
    }
  *p = 0;
  return p - buf;
}


#ifdef DEBUG
void
opendebug(new, shout)
int new, shout;
{
  char buf[256];

#ifdef _MODE_T
  mode_t oumask = umask(0);
#else
  int oumask = umask(0);
#endif

  ASSERT(!dfp);

  (void) mkdir(DEBUGDIR, 0777);
  sprintf(buf, shout ? "%s/SCREEN.%d" : "%s/screen.%d", DEBUGDIR, getpid());
  if (!(dfp = fopen(buf, new ? "w" : "a")))
    dfp = stderr;
  else
    (void)chmod(buf, 0666);

  (void)umask(oumask);
  debug("opendebug: done.\n");
}
#endif /* DEBUG */

void
sleep1000(msec)
int msec;

{
  struct timeval t;

  t.tv_sec = (long) (msec / 1000);
  t.tv_usec = (long) ((msec % 1000) * 1000);
  select(0, (fd_set *)0, (fd_set *)0, (fd_set *)0, &t);
}


#if 0
struct win **
GlobWindows(pattern)
char *pattern;
{
  static struct win *wv[MAXWIN + 1];
  extern struct win *windows;
  struct win *w;
  struct win **av;
  char *p;
  int t, l, ll, i;

  av = wv;

  if (!pattern)
    return NULL;
  debug1("GlobWindows pattern '%s'\n", pattern);
  t = 0;
  p = pattern;
  if (*p == '*')
    {
      t++;
      p++;
    }
  l = strlen(p);
  if (l && p[l - 1] == '*')
    {
      l--;
      p[l] = '\0';
      t += 2;
    }

  for (w = windows; w; w = w->w_next)
    {
      switch (t)
	{
	case 0:         /* exact match */
	  if (!strcmp(p, w->w_title))
	    *av++ = w;
	  break;
	case 1:         /* suffix match */
	  ll = strlen(w->w_title);
	  if (l < ll && !strncmp(p, w->w_title + ll - l, l))
	    *av++ = w;
	  break;
	case 2:         /* prefix match */
	  if (!strncmp(p, w->w_title, l))
	    *av++ = w;
	  break;
	default:        /* 3: infix match */
	  ll = strlen(w->w_title);
	  for (i = ll - l; i >= 0; i--)
	    {
	      if (!strncmp(p, w->w_title + i, l))
		{
		  *av++ = w;
		  break;
		}
	    }
	}
    }
  *av = NULL;
#ifdef DEBUG
  {
    struct win **pp = wv;

    while (--pp >= wv)
      debug1("GlobWindows: '%s'\n", (*pp)->w_title);
  }
#endif
  return (av != wv) ? wv : NULL;
}
#endif

/*
 * This uses either setenv() or putenv(). If it is putenv() we cannot dare
 * to free the buffer after putenv(), unless it it the one found in putenv.c
 */
void
xsetenv(var, value)
char *var;
char *value;
{
#ifndef USESETENV
  char *buf;
  int l;

  if ((buf = (char *)malloc((l = strlen(var)) +
			    strlen(value) + 2)) == NULL)
    {
      Msg(0, strnomem);
      return;
    }
  strcpy(buf, var);
  buf[l] = '=';
  strcpy(buf + l + 1, value);
  putenv(buf);
# ifdef NEEDPUTENV
  /*
   * we use our own putenv(), knowing that it does a malloc()
   * the string space, we can free our buf now.
   */
  free(buf);
# else /* NEEDSETENV */
  /*
   * For all sysv-ish systems that link a standard putenv()
   * the string-space buf is added to the environment and must not
   * be freed, or modified.
   * We are sorry to say that memory is lost here, when setting
   * the same variable again and again.
   */
# endif /* NEEDSETENV */
#else /* USESETENV */
# if defined(linux) || defined(__convex__) || (BSD >= 199103)
  setenv(var, value, 1);
# else
  setenv(var, value);
# endif /* linux || convex || BSD >= 199103 */
#endif /* USESETENV */
}

/*
 *     "$HOST blafoo"          -> "localhost blafoo"
 *     "${HOST}blafoo"         -> "localhostblafoo"
 *     "\$HOST blafoo"         -> "$HOST blafoo"
 *     "\\$HOST blafoo"        -> "\localhost blafoo"
 *     "'$HOST ${HOST}'"       -> "'$HOST ${HOST}'"
 *     "'\$HOST'"              -> "'\$HOST'"
 *     "\'$HOST' $HOST"        -> "'localhost' $HOST"
 *
 *     "$:termcapname:"        -> "termcapvalue"
 *     "$:terminfoname:"       -> "termcapvalue"
 *
 *     "\101"                  -> "A"
 *     "^a"                    -> "\001"
 *
 * display == NULL is valid here!
 */
char *
expand_vars(ss, d)
char *ss;
struct display *d;
{
  static char ebuf[2048];
  int esize = sizeof(ebuf) - 1, vtype, quofl = 0;
  register char *e = ebuf;
  register char *s = ss;
  register char *v;
  char xbuf[11];
  int i;

  while (*s && *s != '\0' && *s != '\n' && esize > 0)
    {
      if (*s == '\'')
	quofl ^= 1;
      if (*s == '$' && !quofl)
	{
	  char *p, c;

	  p = ++s;
	  switch (*s)
	    {
	    case '{':
	      p = ++s;
	      while (*p != '}')
		if (*p++ == '\0')
		  return ss;
	      vtype = 0;                /* env var */
	      break;
	    case ':':
	      p = ++s;
	      while (*p != ':')
		if (*p++ == '\0')
		  return ss;
	      vtype = 1;                /* termcap string */
	      break;
	    default:
	      while ((*p >='a' && *p <= 'z') || (*p >='A' && *p <= 'Z') || (*p >='0' && *p <= '9') || *p == '_')
		p++;
	      vtype = 0;                /* env var */
	    }
	  c = *p;
	  debug1("exp: c='%c'\n", c);
	  *p = '\0';
	  if (vtype == 0)
	    {
	      v = xbuf;
	      if (strcmp(s, "TERM") == 0)
		v = d ? d->d_termname : "unknown";
	      else if (strcmp(s, "COLUMNS") == 0)
		sprintf(xbuf, "%d", d ? d->d_width : -1);
	      else if (strcmp(s, "LINES") == 0)
		sprintf(xbuf, "%d", d ? d->d_height : -1);
	      else
		v = getenv(s);
	    }
	  else
	    v = gettermcapstring(s);
	  if (v)
	    {
	      debug2("exp: $'%s'='%s'\n", s, v);
	      while (*v && esize-- > 0)
		*e++ = *v++;
	    }
	  else
	    debug1("exp: '%s' not env\n", s);  /* '{'-: */
	  if ((*p = c) == '}' || c == ':')
	    p++;
	  s = p;
	}
      else if (*s == '^' && !quofl)
	{
	  s++;
	  i = *s++;
	  if (i == '?')
	    i = '\177';
	  else
	    i &= 0x1f;
	  *e++ = i;
	  esize--;
	}
      else
	{
	  /*
	   * \$, \\$, \\, \\\, \012 are reduced here,
	   * other sequences starting whith \ are passed through.
	   */
	  if (s[0] == '\\' && !quofl)
	    {
	      if (s[1] >= '0' && s[1] <= '7')
		{
		  s++;
		  i = *s - '0';
		  s++;
		  if (*s >= '0' && *s <= '7')
		    {
		      i = i * 8 + *s - '0';
		      s++;
		      if (*s >= '0' && *s <= '7')
			{
			  i = i * 8 + *s - '0';
			  s++;
			}
		    }
		  debug2("expandvars: octal coded character %o (%d)\n", i, i);
		  *e++ = i;
		  esize--;
		  continue;
		}
	      else
		{
		  if (s[1] == '$' ||
		      (s[1] == '\\' && s[2] == '$') ||
		      s[1] == '\'' ||
		      (s[1] == '\\' && s[2] == '\'') ||
		      s[1] == '^' ||
		      (s[1] == '\\' && s[2] == '^'))
		    s++;
		}
	    }
	  *e++ = *s++;
	  esize--;
	}
    }
  if (esize <= 0)
    Msg(0, "expand_vars: buffer overflow\n");
  *e = '\0';
  debug1("expand_var returns '%s'\n", ebuf);
  return ebuf;
}

#ifdef TERMINFO
/*
 * This is a replacement for the buggy _delay function from the termcap
 * emulation of libcurses, which ignores ospeed.
 */
int
_delay(delay, outc)
register int delay;
int (*outc) __P((int));
{
  int pad;
  extern short ospeed;
  static short osp2pad[] = {
    0,2000,1333,909,743,666,500,333,166,83,55,41,20,10,5,2,1,1
  };

  if (ospeed <= 0 || ospeed >= sizeof(osp2pad)/sizeof(*osp2pad))
    return 0;
  pad =osp2pad[ospeed];
  delay = (delay + pad / 2) / pad;
  while (delay-- > 0)
    (*outc)(0);
  return 0;
}

# ifdef linux

/* stupid stupid linux ncurses! It won't to padding with
 * zeros but sleeps instead. This breaks CalcCost, of course.
 * Also, the ncurses wait functions use a global variable
 * to store the current outc function. Oh well...
 */

int (*save_outc) __P((int));

#  undef tputs

void
xtputs(str, affcnt, outc)
char *str;
int affcnt;
int (*outc) __P((int));
{
  extern int tputs __P((const char *, int, int (*)(int)));
  save_outc = outc;
  tputs(str, affcnt, outc);
}

int
_nc_timed_wait(mode, ms, tlp)
int mode, ms, *tlp;
{
  _delay(ms * 10, save_outc);
  return 0;
}

# endif /* linux */

#endif /* TERMINFO */



#ifndef USEVARARGS

# define xva_arg(s, t, tn) (*(t *)(s += xsnoff(tn, 0, 0), s - xsnoff(tn, 0, 0)))
# define xva_list char *

static int
xsnoff(a, b, c)
int a;
char *b;
int c;
{
  return a ? (char *)&c  - (char *)&b : (char *)&b - (char *)&a;
}

int
xsnprintf(s, n, fmt, p1, p2, p3, p4, p5, p6)
char *s;
int n;
char *fmt;
unsigned long p1, p2, p3, p4, p5, p6;
{
  int xvsnprintf __P((char *, int, char *, xva_list));
  return xvsnprintf(s, n, fmt, (char *)&fmt + xsnoff(1, 0, 0));
}

#else

# define xva_arg(s, t, tn) va_arg(s, t)
# define xva_list va_list

#endif


#if !defined(USEVARARGS) || !defined(HAVE_VSNPRINTF)

int
xvsnprintf(s, n, fmt, stack)
char *s;
int n;
char *fmt;
xva_list stack;
{
  char *f, *sf = 0;
  int i, on, argl = 0;
  char myf[10], buf[20];
  char *arg, *myfp;

  on = n;
  f = fmt;
  arg = 0;
  while(arg || (sf = index(f, '%')) || (sf = f + strlen(f)))
    {
      if (arg == 0)
	{
	  arg = f;
	  argl = sf - f;
	}
      if (argl)
	{
	  i = argl > n - 1 ? n - 1 : argl;
	  strncpy(s, arg, i);
          s += i;
	  n -= i;
	  if (i < argl)
	    {
	      *s = 0;
	      return on;
	    }
	}
      arg = 0;
      if (sf == 0)
	continue;
      f = sf;
      sf = 0;
      if (!*f)
	break;
      myfp = myf;
      *myfp++ = *f++;
      while (((*f >= '0' && *f <='9') || *f == '#') && myfp - myf < 8)
        *myfp++ = *f++;
      *myfp++ = *f;
      *myfp = 0;
      if (!*f++)
	break;
      switch(f[-1])
	{
	case '%':
	  arg = "%";
	  break;
	case 'c':
	case 'o':
	case 'd':
	case 'x':
	  i = xva_arg(stack, int, 0);
	  sprintf(buf, myf, i);
	  arg = buf;
	  break;
	case 's':
	  arg = xva_arg(stack, char *, 1);
	  if (arg == 0)
	    arg = "NULL";
	  break;
	default:
	  arg = "";
	  break;
	}
      argl = strlen(arg);
    }
  *s = 0;
  return on - n;
}

#endif