#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <time.h>
#include <alloca.h>
#include <grp.h>
#include <termios.h>
#include <sys/ioctl.h>
#include "lib.h"
#define UUU "unable to "

void w(char *s) { write(2, s, str_len(s)); }
void e(char *x0, char *x1) 
  { w("\nngetty-argv: "); w(x0); w(x1); w("\n"); sleep(1); }
/* sleep is needed to see the erros.  see the option clear also */

unsigned int expand_string(char *tmp, char *string, char *user, char *tty) {
  char *s=string, *pos=tmp, *exp, ch, found_one = 0;
  for (; (ch=*s); s++) {
    if (ch == '%') {
      found_one = 1;
      ch = s[1];
      exp = user;

      switch (ch) {
      case 'T': exp = tty;
      case 'U':
	pos += (tmp) ? str_copy(pos, exp) : str_len(exp);
	++s;
	break;
      case '%': /*  %%U -> %U */
	++s;
      default:
	ch = *s;
	goto non_special;
      }
    } else {
    non_special:
      if (tmp) *pos = ch;
      ++pos;
    }
  }

  if (found_one) {
    if (tmp==0) return pos - tmp + 1;
    else *pos = 0;
  }
  return 0;
}

int main(int argc, char **argv) {
  char **arg=0, **ee, **aa, *in=0, *s, ch;
  char *user, *tty, flagsetsid=0, flagdaemon=0, flagtty=0, *flagpid=0;
  unsigned long n;
  uid_t uid=0;
  gid_t gid=0;

  if (argc<2) {
  usage:
    e("usage:\n\tngetty-argv [Options][--]ArgvString [user [tty]]\n\n",
      "\tArgvString:\t:/bin/sleep:hacker_sleep:39:other:args...\n"
      "\tOptions:\t:-D:-S:-N:-C:-u123:-g59:-a45:-s3:-d/tmp:-r/var/tmp\n"
      "\tOptions:\t:-e,HOME=/,TERM=linux:-p/var/run/ngetty.pid\n"
      "\tExample:\t:-u106:-g506:-d/var/qmail:--:./bin/qmail-qread:queue\n");
    return 100;
  }
  
  user	=(argc>2) ? argv[2] : "";
  tty	=(argc>3) ? argv[3] : "";

  do {
    n = expand_string(in, argv[1], user, tty);
    if (n==0) break;
    in = alloca(n);
  } while (1);
  if (in==0) in=argv[1];

  ch = *in++;	 /* don't use '%' as split char */
  if (ch == 0 || *in == 0) goto usage;
  GLOBAL_split_plus(arg,in,ch, n,5);
  if (n < 2) goto usage;

  for (aa=arg; (s=*aa); aa++) {
    if (*s != '-') break;
    s += 2;
    n=x_atoi(s);
    switch (s[-1]) {
    case 'e':
      ee = 0;
      ch = *s++;
      if (ch) GLOBAL_split(ee,s,ch, n);
      environ = ee;
      break;
    case 'D': flagdaemon=1; break;
    case 'S': flagsetsid=1; break;
    case 'N': flagtty=1; break;
    case 'C': flagtty=2; break;
    case 'p': if (*s) flagpid=s; break;
    case 'g': gid = n; break;
    case 'u': uid = n; break;
    case 'a': alarm(n); break;
    case 's': sleep(n); break;
    case 'd': if (chdir(s))  { e(s, ": chdir error");  return 100; } break;
    case 'r': if (chroot(s)) { e(s, ": chroot error"); return 100; } break;
    case '-': aa++; goto do_it;
    default:
      e(s-2, " : unknown option");
      return 1;
    }
  }

 do_it:
  if (flagdaemon) {
    int pid;
    while ((pid=fork()) <0) sleep(1);
    if (pid) _exit(0);
  }

  if (flagsetsid) setsid();
  if (flagtty==1) ioctl(0, TIOCNOTTY, (void *)1);
  if (flagtty==2) ioctl(0, TIOCSCTTY, (void *)1);

  if (flagpid) {
    int fd = open(flagpid, O_RDWR | O_TRUNC | O_CREAT, 0644);
    if (fd>=0) {
      char tmp[3*sizeof(unsigned long)];
      n=fmt_ulong(tmp, getpid());
      tmp[n] = '\n';
      write(fd, tmp, n+1);
      close(fd);
    }
  }

  if (uid || gid) {
    if (gid==0) gid = uid;
    if (setgroups(1,&gid) ||
	setgid(gid) ||
	setuid(uid)) { e(UUU, "set uidgid"); return 100; }
  }
  
  s = *aa++;
  if (s==0 || *aa==0) return 0;
  if (aa[0][0] == 0) aa[0] = s;

  execve(s, aa, environ);
  e(s, ": exec error");
  return 127;
}
