uses stdio,linux;

{$i smallsh.inc}
(* Буферы программы и рабочие указатели *)
var
  inpbuf:array [0..MAXBUF-1] of char;
  tokbuf:array [0..2*MAXBUF-1] of char;
const
  ptr:pchar=@inpbuf[0];
  tok:pchar=@tokbuf[0];
  special:array [0..5] of char = (' ', #9, '&', ';', #$a, #0);

function runcommand(cline:ppchar;where:integer):integer;forward;
function inarg(c:char):boolean;forward;

(* Вывести приглашение и считать строку *)
function userin(p:pchar):integer;
var
  c, count:integer;
begin
  (* Инициализация для следующих процедур *)
  ptr := inpbuf;
  tok := tokbuf;
  (* Вывести приглашение *)
  write(p);
  count := 0;
  while true do
  begin
    c := getchar;
    if c = EOF then
    begin
      userin:=EOF;
      exit;
    end;
    if count < MAXBUF then
    begin
      inpbuf[count] := char(c);
      inc(count);
    end;
    if (c = $a) and (count < MAXBUF) then
    begin
      inpbuf[count] := #0;
      userin:=count;
      exit;
    end;
    (* Если строка слишком длинная, начать снова *)
    if c = $a then
    begin
      writeln ('smallsh: слишком длинная входная строка');
      count := 0;
      write (p);
    end;
  end;
end;

(* Получить лексему и поместить ее в буфер tokbuf *)
function gettok (outptr:ppchar):integer;
var
  _type:integer;
begin
  (* Присвоить указателю на строку outptr значение tok *)
  outptr^ := tok;
  (* Удалить пробелы из буфера, содержащего лексемы *)
  while (ptr^ = ' ') or (ptr^ = #9) do
    inc(ptr);
  (* Установить указатель на первую лексему в буфере *)
  tok^ := ptr^;
  inc(tok);
  (* Установить значение переменной type в соответствии
   * с типом лексемы в буфере *)
  case ptr^ of
    #$a:
    begin
      _type := EOL;
      inc(ptr);
    end;
    '&':
    begin
      _type := AMPERSAND;
      inc(ptr);
    end;
    ';':
    begin
      _type := SEMICOLON;
      inc(ptr);
    end;
    else
    begin
      _type := ARG;
      inc(ptr);
      (* Продолжить чтение обычных символов *)
      while inarg (ptr^) do
      begin
        tok^ := ptr^;
	inc(tok);
	inc(ptr);
      end;
    end;
  end;
  tok^ := #0;
  inc(tok);
  gettok:=_type;
end;


function inarg(c:char):boolean;
var
  wrk:pchar;
begin
  wrk := special;
  while wrk^<>#0 do
  begin
    if c = wrk^ then
    begin
      inarg:=false;
      exit;
    end;
    inc(wrk);
  end;
  inarg:=true;
end;

function procline:integer;	(* обработка строки ввода *)
var
  arg:array [0..MAXARG] of pchar;	(* массив указателей для runcommand *)
  toktype:integer;			(* тип лексемы в команде *)
  narg:integer;				(* число аргументов *)
  _type:integer;			(* на переднем плане или в фоне *)
begin
  narg := 0;
  while true do			(* бесконечный цикл *)
  begin
    (* Выполнить действия в зависимости от типа лексемы *)
    toktype := gettok (@arg[narg]);
    case toktype of
      2://ARG
        if narg < MAXARG then
          inc(narg);
      1,3,4://EOL,SEMICOLON, AMPERSAND:
      begin
        if toktype = AMPERSAND then
          _type := BACKGROUND
        else
          _type := FOREGROUND;
        if narg <> 0 then
        begin
          arg[narg] := nil;
          runcommand (arg, _type);
        end;
        if toktype = EOL then
	  exit;
        narg := 0;
      end;
    end;
  end;
end;

(* Выполнить команду, возможно ожидая ее завершения *)
function runcommand(cline:ppchar;where:integer):integer;
var
  pid:longint;
  status:integer;
begin
  pid := fork;
  case pid of
    -1:
    begin
      perror ('smallsh');
      runcommand:=-1;
      exit;
    end;
    0:
    begin
      execvp (cline^, cline, envp);
      perror (cline^);
      halt(1);
    end;
  end;

  (* Код родительского процесса *)
  (* Если это фоновый процесс, вывести pid и выйти *)
  if where = BACKGROUND then
  begin
    writeln ('[Идентификатор процесса ',pid,']');
    runcommand:=0;
    exit;
  end;

  (* Ожидание завершения процесса с идентификатором pid *)
  if waitpid (pid, @status, 0) = -1 then
    runcommand:=-1
  else
    runcommand:=status;
end;

const
  prompt = 'Command> ';	(* приглашение ввода командной строки *)
begin
  while userin (prompt) <> EOF do
    procline;
end.
