1/* Copyright (c) 1993-2002
2 *      Juergen Weigert (jnweiger@immd4.informatik.uni-erlangen.de)
3 *      Michael Schroeder (mlschroe@immd4.informatik.uni-erlangen.de)
4 * Copyright (c) 1987 Oliver Laumann
5 *
6 * This program is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 2, or (at your option)
9 * any later version.
10 *
11 * This program is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
14 * GNU General Public License for more details.
15 *
16 * You should have received a copy of the GNU General Public License
17 * along with this program (see the file COPYING); if not, write to the
18 * Free Software Foundation, Inc.,
19 * 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA
20 *
21 ****************************************************************
22 */
23
24#include <sys/types.h>
25#include <sys/stat.h>	/* mkdir() declaration */
26#include <signal.h>
27
28#include "config.h"
29#include "screen.h"
30#include "extern.h"
31
32#ifdef SVR4
33# include <sys/resource.h>
34#endif
35
36extern struct layer *flayer;
37
38extern int eff_uid, real_uid;
39extern int eff_gid, real_gid;
40extern struct mline mline_old;
41extern struct mchar mchar_blank;
42extern unsigned char *null, *blank;
43
44#ifdef HAVE_FDWALK
45static int close_func __P((void *, int));
46#endif
47
48char *
49SaveStr(str)
50register const char *str;
51{
52  register char *cp;
53
54  if ((cp = malloc(strlen(str) + 1)) == NULL)
55    Panic(0, strnomem);
56  else
57    strcpy(cp, str);
58  return cp;
59}
60
61char *
62SaveStrn(str, n)
63register const char *str;
64int n;
65{
66  register char *cp;
67
68  if ((cp = malloc(n + 1)) == NULL)
69    Panic(0, strnomem);
70  else
71    {
72      bcopy((char *)str, cp, n);
73      cp[n] = 0;
74    }
75  return cp;
76}
77
78/* cheap strstr replacement */
79char *
80InStr(str, pat)
81char *str;
82const char *pat;
83{
84  int npat = strlen(pat);
85  for (;*str; str++)
86    if (!strncmp(str, pat, npat))
87      return str;
88  return 0;
89}
90
91#ifndef HAVE_STRERROR
92char *
93strerror(err)
94int err;
95{
96  extern int sys_nerr;
97  extern char *sys_errlist[];
98
99  static char er[20];
100  if (err > 0 && err < sys_nerr)
101    return sys_errlist[err];
102  sprintf(er, "Error %d", err);
103  return er;
104}
105#endif
106
107void
108centerline(str, y)
109char *str;
110int y;
111{
112  int l, n;
113
114  ASSERT(flayer);
115  n = strlen(str);
116  if (n > flayer->l_width - 1)
117    n = flayer->l_width - 1;
118  l = (flayer->l_width - 1 - n) / 2;
119  LPutStr(flayer, str, n, &mchar_blank, l, y);
120}
121
122void
123leftline(str, y)
124char *str;
125int y;
126{
127  int l, n;
128  struct mchar mchar_dol;
129
130  mchar_dol = mchar_blank;
131  mchar_dol.image = '$';
132
133  ASSERT(flayer);
134  l = n = strlen(str);
135  if (n > flayer->l_width - 1)
136    n = flayer->l_width - 1;
137  LPutStr(flayer, str, n, &mchar_blank, 0, y);
138  if (n != l)
139    LPutChar(flayer, &mchar_dol, n, y);
140}
141
142
143char *
144Filename(s)
145char *s;
146{
147  register char *p = s;
148
149  if (p)
150    while (*p)
151      if (*p++ == '/')
152        s = p;
153  return s;
154}
155
156char *
157stripdev(nam)
158char *nam;
159{
160#ifdef apollo
161  char *p;
162
163  if (nam == NULL)
164    return NULL;
165# ifdef SVR4
166  /* unixware has /dev/pts012 as synonym for /dev/pts/12 */
167  if (!strncmp(nam, "/dev/pts", 8) && nam[8] >= '0' && nam[8] <= '9')
168    {
169      static char b[13];
170      sprintf(b, "pts/%d", atoi(nam + 8));
171      return b;
172    }
173# endif /* SVR4 */
174  if (p = strstr(nam,"/dev/"))
175    return p + 5;
176#else /* apollo */
177  if (nam == NULL)
178    return NULL;
179  if (strncmp(nam, "/dev/", 5) == 0)
180    return nam + 5;
181#endif /* apollo */
182  return nam;
183}
184
185
186/*
187 *    Signal handling
188 */
189
190#ifdef POSIX
191sigret_t (*xsignal(sig, func))
192# ifndef __APPLE__
193 __P(SIGPROTOARG)
194# else
195()
196# endif
197int sig;
198sigret_t (*func) __P(SIGPROTOARG);
199{
200  struct sigaction osa, sa;
201  sa.sa_handler = func;
202  (void)sigemptyset(&sa.sa_mask);
203#ifdef SA_RESTART
204  sa.sa_flags = (sig == SIGCHLD ? SA_RESTART : 0);
205#else
206  sa.sa_flags = 0;
207#endif
208  if (sigaction(sig, &sa, &osa))
209    return (sigret_t (*)__P(SIGPROTOARG))-1;
210  return osa.sa_handler;
211}
212
213#else
214# ifdef hpux
215/*
216 * hpux has berkeley signal semantics if we use sigvector,
217 * but not, if we use signal, so we define our own signal() routine.
218 */
219void (*xsignal(sig, func)) __P(SIGPROTOARG)
220int sig;
221void (*func) __P(SIGPROTOARG);
222{
223  struct sigvec osv, sv;
224
225  sv.sv_handler = func;
226  sv.sv_mask = sigmask(sig);
227  sv.sv_flags = SV_BSDSIG;
228  if (sigvector(sig, &sv, &osv) < 0)
229    return (void (*)__P(SIGPROTOARG))(BADSIG);
230  return osv.sv_handler;
231}
232# endif	/* hpux */
233#endif	/* POSIX */
234
235
236/*
237 *    uid/gid handling
238 */
239
240#ifdef HAVE_SETEUID
241
242void
243xseteuid(euid)
244int euid;
245{
246  if (seteuid(euid) == 0)
247    return;
248  seteuid(0);
249  if (seteuid(euid))
250    Panic(errno, "seteuid");
251}
252
253void
254xsetegid(egid)
255int egid;
256{
257  if (setegid(egid))
258    Panic(errno, "setegid");
259}
260
261#else /* HAVE_SETEUID */
262# ifdef HAVE_SETREUID
263
264void
265xseteuid(euid)
266int euid;
267{
268  int oeuid;
269
270  oeuid = geteuid();
271  if (oeuid == euid)
272    return;
273  if ((int)getuid() != euid)
274    oeuid = getuid();
275  if (setreuid(oeuid, euid))
276    Panic(errno, "setreuid");
277}
278
279void
280xsetegid(egid)
281int egid;
282{
283  int oegid;
284
285  oegid = getegid();
286  if (oegid == egid)
287    return;
288  if ((int)getgid() != egid)
289    oegid = getgid();
290  if (setregid(oegid, egid))
291    Panic(errno, "setregid");
292}
293
294# endif /* HAVE_SETREUID */
295#endif /* HAVE_SETEUID */
296
297
298
299#ifdef NEED_OWN_BCOPY
300void
301xbcopy(s1, s2, len)
302register char *s1, *s2;
303register int len;
304{
305  if (s1 < s2 && s2 < s1 + len)
306    {
307      s1 += len;
308      s2 += len;
309      while (len-- > 0)
310	*--s2 = *--s1;
311    }
312  else
313    while (len-- > 0)
314      *s2++ = *s1++;
315}
316#endif	/* NEED_OWN_BCOPY */
317
318void
319bclear(p, n)
320char *p;
321int n;
322{
323  bcopy((char *)blank, p, n);
324}
325
326
327void
328Kill(pid, sig)
329int pid, sig;
330{
331  if (pid < 2)
332    return;
333  (void) kill(pid, sig);
334}
335
336#ifdef HAVE_FDWALK
337/*
338 * Modern versions of Solaris include fdwalk(3c) which allows efficient
339 * implementation of closing open descriptors; this is helpful because
340 * the default file descriptor limit has risen to 65k.
341 */
342static int
343close_func(cb_data, fd)
344void *cb_data;
345int fd;
346{
347  int except = *(int *)cb_data;
348  if (fd > 2 && fd != except)
349    (void)close(fd);
350  return (0);
351}
352
353void
354closeallfiles(except)
355int except;
356{
357  (void)fdwalk(close_func, &except);
358}
359
360#else /* HAVE_FDWALK */
361
362void
363closeallfiles(except)
364int except;
365{
366  int f;
367#ifdef SVR4
368  struct rlimit rl;
369
370  if ((getrlimit(RLIMIT_NOFILE, &rl) == 0) && rl.rlim_max != RLIM_INFINITY)
371    f = rl.rlim_max;
372  else
373#endif /* SVR4 */
374#if defined(SYSV) && defined(NOFILE) && !defined(ISC)
375  f = NOFILE;
376#else /* SYSV && !ISC */
377  f = getdtablesize();
378#endif /* SYSV && !ISC */
379  while (--f > 2)
380    if (f != except)
381      close(f);
382}
383
384#endif /* HAVE_FDWALK */
385
386
387/*
388 *  Security - switch to real uid
389 */
390
391#ifndef USE_SETEUID
392static int UserPID;
393static sigret_t (*Usersigcld)__P(SIGPROTOARG);
394#endif
395static int UserSTAT;
396
397int
398UserContext()
399{
400#ifndef USE_SETEUID
401  if (eff_uid == real_uid && eff_gid == real_gid)
402    return 1;
403  Usersigcld = signal(SIGCHLD, SIG_DFL);
404  debug("UserContext: forking.\n");
405  switch (UserPID = fork())
406    {
407    case -1:
408      Msg(errno, "fork");
409      return -1;
410    case 0:
411      signal(SIGHUP, SIG_DFL);
412      signal(SIGINT, SIG_IGN);
413      signal(SIGQUIT, SIG_DFL);
414      signal(SIGTERM, SIG_DFL);
415# ifdef BSDJOBS
416      signal(SIGTTIN, SIG_DFL);
417      signal(SIGTTOU, SIG_DFL);
418# endif
419      setuid(real_uid);
420      setgid(real_gid);
421      return 1;
422    default:
423      return 0;
424    }
425#else
426  xseteuid(real_uid);
427  xsetegid(real_gid);
428  return 1;
429#endif
430}
431
432void
433UserReturn(val)
434int val;
435{
436#ifndef USE_SETEUID
437  if (eff_uid == real_uid && eff_gid == real_gid)
438    UserSTAT = val;
439  else
440    _exit(val);
441#else
442  xseteuid(eff_uid);
443  xsetegid(eff_gid);
444  UserSTAT = val;
445#endif
446}
447
448int
449UserStatus()
450{
451#ifndef USE_SETEUID
452  int i;
453# ifdef BSDWAIT
454  union wait wstat;
455# else
456  int wstat;
457# endif
458
459  if (eff_uid == real_uid && eff_gid == real_gid)
460    return UserSTAT;
461  if (UserPID < 0)
462    return -1;
463  while ((errno = 0, i = wait(&wstat)) != UserPID)
464    if (i < 0 && errno != EINTR)
465      break;
466  (void) signal(SIGCHLD, Usersigcld);
467  if (i == -1)
468    return -1;
469  return WEXITSTATUS(wstat);
470#else
471  return UserSTAT;
472#endif
473}
474
475#ifndef HAVE_RENAME
476int
477rename (old, new)
478char *old;
479char *new;
480{
481  if (link(old, new) < 0)
482    return -1;
483  return unlink(old);
484}
485#endif
486
487
488int
489AddXChar(buf, ch)
490char *buf;
491int ch;
492{
493  char *p = buf;
494
495  if (ch < ' ' || ch == 0x7f)
496    {
497      *p++ = '^';
498      *p++ = ch ^ 0x40;
499    }
500  else if (ch >= 0x80)
501    {
502      *p++ = '\\';
503      *p++ = (ch >> 6 & 7) + '0';
504      *p++ = (ch >> 3 & 7) + '0';
505      *p++ = (ch >> 0 & 7) + '0';
506    }
507  else
508    *p++ = ch;
509  return p - buf;
510}
511
512int
513AddXChars(buf, len, str)
514char *buf, *str;
515int len;
516{
517  char *p;
518
519  if (str == 0)
520    {
521      *buf = 0;
522      return 0;
523    }
524  len -= 4;     /* longest sequence produced by AddXChar() */
525  for (p = buf; p < buf + len && *str; str++)
526    {
527      if (*str == ' ')
528        *p++ = *str;
529      else
530        p += AddXChar(p, *str);
531    }
532  *p = 0;
533  return p - buf;
534}
535
536
537#ifdef DEBUG
538void
539opendebug(new, shout)
540int new, shout;
541{
542  char buf[256];
543
544#ifdef _MODE_T
545  mode_t oumask = umask(0);
546#else
547  int oumask = umask(0);
548#endif
549
550  ASSERT(!dfp);
551
552  (void) mkdir(DEBUGDIR, 0777);
553  sprintf(buf, shout ? "%s/SCREEN.%d" : "%s/screen.%d", DEBUGDIR, getpid());
554  if (!(dfp = fopen(buf, new ? "w" : "a")))
555    dfp = stderr;
556  else
557    (void)chmod(buf, 0666);
558
559  (void)umask(oumask);
560  debug("opendebug: done.\n");
561}
562#endif /* DEBUG */
563
564void
565sleep1000(msec)
566int msec;
567
568{
569  struct timeval t;
570
571  t.tv_sec = (long) (msec / 1000);
572  t.tv_usec = (long) ((msec % 1000) * 1000);
573  select(0, (fd_set *)0, (fd_set *)0, (fd_set *)0, &t);
574}
575
576
577/*
578 * This uses either setenv() or putenv(). If it is putenv() we cannot dare
579 * to free the buffer after putenv(), unless it it the one found in putenv.c
580 */
581void
582xsetenv(var, value)
583char *var;
584char *value;
585{
586#ifndef USESETENV
587  char *buf;
588  int l;
589
590  if ((buf = (char *)malloc((l = strlen(var)) +
591			    strlen(value) + 2)) == NULL)
592    {
593      Msg(0, strnomem);
594      return;
595    }
596  strcpy(buf, var);
597  buf[l] = '=';
598  strcpy(buf + l + 1, value);
599  putenv(buf);
600# ifdef NEEDPUTENV
601  /*
602   * we use our own putenv(), knowing that it does a malloc()
603   * the string space, we can free our buf now.
604   */
605  free(buf);
606# else /* NEEDSETENV */
607  /*
608   * For all sysv-ish systems that link a standard putenv()
609   * the string-space buf is added to the environment and must not
610   * be freed, or modified.
611   * We are sorry to say that memory is lost here, when setting
612   * the same variable again and again.
613   */
614# endif /* NEEDSETENV */
615#else /* USESETENV */
616# if defined(linux) || defined(__convex__) || (BSD >= 199103)
617  setenv(var, value, 1);
618# else
619  setenv(var, value);
620# endif /* linux || convex || BSD >= 199103 */
621#endif /* USESETENV */
622}
623
624#ifdef TERMINFO
625/*
626 * This is a replacement for the buggy _delay function from the termcap
627 * emulation of libcurses, which ignores ospeed.
628 */
629int
630_delay(delay, outc)
631register int delay;
632int (*outc) __P((int));
633{
634  int pad;
635  extern short ospeed;
636  static short osp2pad[] = {
637    0,2000,1333,909,743,666,500,333,166,83,55,41,20,10,5,2,1,1
638  };
639
640  if (ospeed <= 0 || ospeed >= (int)(sizeof(osp2pad)/sizeof(*osp2pad)))
641    return 0;
642  pad =osp2pad[ospeed];
643  delay = (delay + pad / 2) / pad;
644  while (delay-- > 0)
645    (*outc)(0);
646  return 0;
647}
648
649# ifdef linux
650
651/* stupid stupid linux ncurses! It won't to padding with
652 * zeros but sleeps instead. This breaks CalcCost, of course.
653 * Also, the ncurses wait functions use a global variable
654 * to store the current outc function. Oh well...
655 */
656
657int (*save_outc) __P((int));
658
659#  undef tputs
660
661void
662xtputs(str, affcnt, outc)
663char *str;
664int affcnt;
665int (*outc) __P((int));
666{
667  extern int tputs __P((const char *, int, int (*)(int)));
668  save_outc = outc;
669  tputs(str, affcnt, outc);
670}
671
672int
673_nc_timed_wait(mode, ms, tlp)
674int mode, ms, *tlp;
675{
676  _delay(ms * 10, save_outc);
677  return 0;
678}
679
680# endif /* linux */
681
682#endif /* TERMINFO */
683
684
685
686#ifndef USEVARARGS
687
688# define xva_arg(s, t, tn) (*(t *)(s += xsnoff(tn, 0, 0), s - xsnoff(tn, 0, 0)))
689# define xva_list char *
690
691static int
692xsnoff(a, b, c)
693int a;
694char *b;
695int c;
696{
697  return a ? (char *)&c  - (char *)&b : (char *)&b - (char *)&a;
698}
699
700int
701xsnprintf(s, n, fmt, p1, p2, p3, p4, p5, p6)
702char *s;
703int n;
704char *fmt;
705unsigned long p1, p2, p3, p4, p5, p6;
706{
707  int xvsnprintf __P((char *, int, char *, xva_list));
708  return xvsnprintf(s, n, fmt, (char *)&fmt + xsnoff(1, 0, 0));
709}
710
711#else
712
713# define xva_arg(s, t, tn) va_arg(s, t)
714# define xva_list va_list
715
716#endif
717
718
719#if !defined(USEVARARGS) || !defined(HAVE_VSNPRINTF)
720
721int
722xvsnprintf(s, n, fmt, stack)
723char *s;
724int n;
725char *fmt;
726xva_list stack;
727{
728  char *f, *sf = 0;
729  int i, on, argl = 0;
730  char myf[10], buf[20];
731  char *arg, *myfp;
732
733  on = n;
734  f = fmt;
735  arg = 0;
736  while(arg || (sf = index(f, '%')) || (sf = f + strlen(f)))
737    {
738      if (arg == 0)
739	{
740	  arg = f;
741	  argl = sf - f;
742	}
743      if (argl)
744	{
745	  i = argl > n - 1 ? n - 1 : argl;
746	  strncpy(s, arg, i);
747          s += i;
748	  n -= i;
749	  if (i < argl)
750	    {
751	      *s = 0;
752	      return on;
753	    }
754	}
755      arg = 0;
756      if (sf == 0)
757	continue;
758      f = sf;
759      sf = 0;
760      if (!*f)
761	break;
762      myfp = myf;
763      *myfp++ = *f++;
764      while (((*f >= '0' && *f <='9') || *f == '#') && myfp - myf < 8)
765        *myfp++ = *f++;
766      *myfp++ = *f;
767      *myfp = 0;
768      if (!*f++)
769	break;
770      switch(f[-1])
771	{
772	case '%':
773	  arg = "%";
774	  break;
775	case 'c':
776	case 'o':
777	case 'd':
778	case 'x':
779	  i = xva_arg(stack, int, 0);
780	  sprintf(buf, myf, i);
781	  arg = buf;
782	  break;
783	case 's':
784	  arg = xva_arg(stack, char *, 1);
785	  if (arg == 0)
786	    arg = "NULL";
787	  break;
788	default:
789	  arg = "";
790	  break;
791	}
792      argl = strlen(arg);
793    }
794  *s = 0;
795  return on - n;
796}
797
798#endif
799