﻿/* //next lines (up to first line with '* /') are used for including to debugging program

use io,proc,ui

.kybldebuggerinput=proc.pipe('KyblDebuggerOut',false);
.kybldebuggeroutput=proc.pipe('KyblDebuggerIn',false);
.kybldebuggerfdepth=0;
.kybldebuggermaxfdepth=99999;
.kybldebuggermaxbdepth=99999;
.kybldebuggergotoline=null;

function KyblDebugInfo(linenum,blockdepth,fileposition=0,variables=[])
  if isstr(linenum) then //there is some message, not line number
    case linenum
      in 'quit': io.writem(.kybldebuggeroutput,['quit']);
      else throw null;
    end;
  else   //send line # and depth
    if .kybldebuggerfdepth > .kybldebuggermaxfdepth or (.kybldebuggerfdepth = .kybldebuggermaxfdepth and blockdepth > .kybldebuggermaxbdepth) then
      return;
    end;

    if .kybldebuggergotoline # null then
      if .kybldebuggergotoline # linenum then
        return;
      else
        .kybldebuggergotoline=null;
      end;
    end;

    io.writem(.kybldebuggeroutput,['breakpoint',linenum,.kybldebuggerfdepth,fileposition]);
    while true do
      response=io.readm(.kybldebuggerinput);
      case response[0]
        in 'showvariables':
          io.writem(.kybldebuggeroutput,['variablesstart',len(variables)]);
          names=keys(variables);
          i=0;
          for variable in variables do
            try
              io.writem(.kybldebuggeroutput,['variable',names[i],variable]);
            catch e by
              io.writem(.kybldebuggeroutput,['variable',names[i],'Contains native object']);
            end;
            i++;
          end;
        in 'showoutput':
          while true do
            ui.keys(true);
            key=ui.cmd();
            if not isarray(key) and key < 0 then
              break;
            end;
          end;
          io.writem(.kybldebuggeroutput,['ok']);
        in 'stepover':
          .kybldebuggermaxfdepth=.kybldebuggerfdepth;
          .kybldebuggermaxbdepth=99999;
          break;
        in 'stepinto':
          .kybldebuggermaxfdepth=99999;
          .kybldebuggermaxbdepth=99999;
          break;
        in 'outoffunc':
          .kybldebuggermaxfdepth=.kybldebuggerfdepth-1;
          .kybldebuggermaxbdepth=99999;
          break;
        in 'outofblock':
          .kybldebuggermaxfdepth=.kybldebuggerfdepth;
          .kybldebuggermaxbdepth=blockdepth-1;
          break;
        in 'runtothisline':
          .kybldebuggermaxfdepth=99999;
          .kybldebuggermaxbdepth=99999;
          .kybldebuggergotoline=linenum;
          break;
        else print response; throw response;
      end;
    end;
    if io.avail(.kybldebuggerinput) # 0 then
           //some waste in pipe
      io.read(.kybldebuggerinput,io.avail(.kybldebuggerinput));
ui.msg('Some waste in pipe.');
    end;
  end;
end;

*/


use array,files,graph,io,math,proc,ui

const fontsize=13;
const textpadding=1;

.thisprogram='KyblDebugger';
.debuggerversion='1.3';
.lastused=[];
.maxlastused=3;
.debugprogram=null;
.kybldebuglines=null;
.displayeddebugapp=true;
.line_ignoreuntil=null;
.actualdepth=null;
.insidefunction=null;
.iscomment=null;
.activevariables=null;
.watchingvariables=true;
.programstoremove=[];

function LoadSettings()
  try
    f=io.open(.thisprogram+'.cfg');
    if f = null then return false; end;
    .keys=io.readm(f);
    .lastused=io.readm(f);
    .watchingvariables=io.readm(f);
    io.close(f);
  catch e by
    return false;
  end;

  return true;
end;

function SaveSettings()
  try
    f=io.create(.thisprogram+'.cfg');
    io.writem(f,.keys);
    io.writem(f,.lastused);
    io.writem(f,.watchingvariables);
    io.close(f);
  catch e by
    return false;
  end;

  return true;
end;

function SetKeys()
  types=['stepover': 'Step over','stepinto': 'Step into','outoffunc': 'Step out of function','outofblock': 'Step out of block','runtothisline': 'Run to this line','showoutput': 'Show output','showvariables': 'Show variables','editfile': 'Edit file','quit': 'Quit debugging'];
  .keys=[];

  ts=keys(types);
  for t in ts do
    while true do
      io.write(io.stdout,'Press key to: '+types[t]);
      do
        ui.keys(false);
        key=ui.cmd();
      until key >= 0;
      io.writeln(io.stdout,' <'+key+'>');

        //check if exists already
      alreadyused=false;
      for t2 in ts do
        if t2 = t then  //only previous
          break;
        end;
        if .keys[t2] = key then
          alreadyused=true;
          ui.msg('Key is already used for action "'+types[t2]+'". Please press other key.','Key used');
          break;
        end;
      end;
      if alreadyused = false then
        break;
      end;
    end;
    .keys[t]=key;
  end;
  io.writeln(io.stdout,'');
end;

function PrintCode(linenum)
  lines=math.floor(graph.size()[1]/(.fontsize+2*.textpadding))-1;
  graph.font(['LatinPlain12',.fontsize,false,false]);

  startline=linenum-math.floor((lines-1)/2);
  if startline < 0 then
    startline=0;
  end;

  graph.clear();

  for i=0 to lines-1 do
    if i+startline = len(.kybldebuglines) then
      break;
    end;
    if i+startline = linenum then
      graph.brush(0x30D0FF);
      graph.pen(0x30D0FF);
      graph.rect(0,i*(2*.textpadding+.fontsize)+.textpadding,graph.size()[0],2*.textpadding+.fontsize);
      graph.pen(graph.black);
    end;
    graph.text(5,i*(2*.textpadding+.fontsize)+.textpadding+.fontsize,.kybldebuglines[startline+i]);
  end;

  graph.show();
end;

function Breakpoint(linenum,depth)
  PrintCode(linenum);

  graph.brush(0xFFD030);
  graph.pen(0xFFD030);
  graph.rect(0,graph.size()[1]-1.2*.fontsize,graph.size()[0],1.2*.fontsize);
  graph.pen(graph.black);
  graph.text(5,graph.size()[1]-.fontsize/3,'Line '+(linenum+1)+', depth '+depth);
  if .displayeddebugapp then
    proc.show(.thisprogram);
  end;

  while true do
    ui.keys(false);
    key=ui.cmd();
    .displayeddebugapp=false;
    keyfunc=array.index(.keys,key);
    if keyfunc # -1 then
      keyfunc=keys(.keys)[keyfunc];
      return keyfunc; //stepinto etc.
    end;
  end;
end;

function IsntIgnored(content,onlyremove=false)
  if .iscomment then
    return false;
  end;

  if len(.line_ignoreuntil) > 0 then
    last=len(.line_ignoreuntil)-1;
    if content = .line_ignoreuntil[last] then
      array.remove(.line_ignoreuntil,last);
    end;
    if onlyremove then
      return true;
    end;

    return false;
  end;

  return true;
end;

function ShiftBy(line,length,data)
  if not data['valid'] then
    data['position']+=length;
  end;

  return substr(line,length);
end;

function SetActiveVariables(data)
  if data['activevariables_line'] = null then
    activevariables_line='';
    for variable in .activevariables do
      activevariables_line=activevariables_line+'\''+variable+'\':'+variable+',';
    end;
    if len(activevariables_line) > 0 then
      activevariables_line=substr(activevariables_line,0,len(activevariables_line)-1);
    end;
    data['activevariables_line']=activevariables_line;
  end;
end;

function SetNewVariable(variable)
  pos=array.index(.activevariables,variable);
  if pos # -1 then
    array.remove(.activevariables,pos);
 end;
 array.insert(.activevariables,0,variable);
end;

function GetValidPosOfLine(line,watchingvariables)
  l=line;
  data=['valid': false,'position': 0,'activevariables_line': null];

  while true do
    prevpos=data['position'];
    if len(l) = 0 then break; end;
    ch=code(substr(l,0,1))[0];
    l=ShiftBy(l,1,data);

    if ch = code('*')[0] then
          //check if end of comment
      if len(l) > 0 then
        if substr(l,0,1) = '/' and .iscomment then
          .iscomment=false;
          l=ShiftBy(l,1,data);
        end;
      end;
      if IsntIgnored(char(ch)) then
        data['position']=prevpos;
        data['valid']=true;
        SetActiveVariables(data);
      end;
    elsif .iscomment then
             //nothing
    elsif ch = code(' ')[0] or ch = code('	')[0] or ch = code(';')[0] then
             //nothing
    elsif ch = code('=')[0] then
      if watchingvariables and IsntIgnored(char(ch),true) and len(.line_ignoreuntil) = 0 then
  //not in cond., it's variable setting
        variable=trim(substr(line,0,len(line)-len(l)-1));
        pos=rindex(variable,';');
        if pos # -1 then
          variable=substr(variable,pos+1);
        end;
        pos=rindex(variable,']');
        if pos # -1 then
          pos2=index(variable,'[');
          if pos2 # -1 and pos2 < pos then
            variable=trim(delete(variable,pos2,pos-pos2+1));
          end;
        end;
        case substr(variable,len(variable)-1)
          in '+','-','*','/': variable=trim(substr(variable,0,len(variable)-1));
        end;
        pos=rindex(variable,' ');
        if pos # -1 then
          variable=substr(variable,pos+1);
        end;
        if not .insidefunction and substr(variable,0,1) # '.' then
          variable='.'+variable;
        end;

        SetNewVariable(variable);
      end;
    elsif ch = code('(')[0] then
      if IsntIgnored(char(ch),true) then
        append(.line_ignoreuntil,')');
      end;
    elsif ch = code('[')[0] then
      if IsntIgnored(char(ch),true) then
        append(.line_ignoreuntil,']');
      end;
    elsif ch = code(')')[0] then
      IsntIgnored(char(ch));
    elsif ch = code(']')[0] then
      IsntIgnored(char(ch));
    elsif ch = code('/')[0] then
                //check if comment
      if len(l) > 0 then
        case substr(l,0,1)
          in '*': .iscomment=true;
          in '/': break;
             //comment to end of line
        end;
      end;
      if IsntIgnored(char(ch)) then
        data['position']=prevpos;
        data['valid']=true;
        SetActiveVariables(data);
      end;
    elsif ch = code('\'')[0] or ch = code('\"')[0] then
      while len(l) > 0 do
        pos=index(l,char(ch));
        if pos = -1 then
          l=''; break;
        end;

        i=pos-1;
        while i >= 0 and substr(l,i,1) = '\\' do
          i--;
        end; 
        l=ShiftBy(l,pos+1,data);
        if (pos-i+1)%2 = 0 then
        //not escaped, end of string
          break;
        end;
      end;

      if IsntIgnored(char(ch)) then
        data['position']=prevpos;
        data['valid']=true;
        SetActiveVariables(data);
      end;
    else
      string=char([ch]);
      if len(l) = 0 then
        ch=-1;
      else
        ch=code(substr(l,0,1))[0];
      end;
      while (ch >= code('a')[0] and ch <= code('z')[0]) or (ch >= code('A')[0] and ch <= code('Z')[0]) or (ch >= code('0')[0] and ch <= code('9')[0]) do
        string=string+char([ch]);
        l=ShiftBy(l,1,data);
        if len(l) = 0 then
          ch=-1;
        else
          ch=code(substr(l,0,1))[0];
        end;
      end;

      case string
        in 'case':
          .actualdepth++;
          if IsntIgnored(string) then
            data['position']=prevpos;
            data['valid']=true;
            SetActiveVariables(data);
          end;
          append(.line_ignoreuntil,'in');
        in 'catch':
          IsntIgnored(string);
          append(.line_ignoreuntil,'by');
        in 'do':
          if IsntIgnored(string) then
            .actualdepth++;
          end;
        in 'else':   //nothing
        in 'elsif':
          IsntIgnored(string);
          append(.line_ignoreuntil,'then');
        in 'end':   //nothing
          if .insidefunction and .actualdepth = 1 then
            if data['depthdecr_in'] = null then
              data['depthdecr_in']=[];
            end;
            append(data['depthdecr_in'],len(line)-len(l)-len('end'));
              //remove local vars
            for i=len(.activevariables)-1 to 0 by -1 do
              if substr(.activevariables[i],0,1) # '.' then
                array.remove(.activevariables,i);
              end;
            end;
            .insidefunction=false;
          end;
          if .actualdepth < 1 then
            print 'error on line '+line+')';
          end;
          .actualdepth--;
        in 'for':
          .actualdepth++;
          if IsntIgnored(string) then
            data['position']=prevpos;
            data['valid']=true;
            SetActiveVariables(data);
          end;
          append(.line_ignoreuntil,'do');

          if watchingvariables then
            vartmp=trim(l);
            pos=index(vartmp,' ');
            if pos # -1 then
              vartmp=substr(vartmp,0,pos);
            end;
            pos=index(vartmp,'=');
            if pos # -1 then
              vartmp=substr(vartmp,0,pos);
            end;
            if vartmp # '' then
              SetNewVariable(vartmp);
            end;
          end;
        in 'forward':
          .actualdepth--;
          array.remove(data,'depthincr');
          .insidefunction=false;
              //remove local vars
          for i=len(.activevariables)-1 to 0 by -1 do
            if substr(.activevariables[i],0,1) # '.' then
              array.remove(.activevariables,i);
            end;
          end;
        in 'function':
          append(.line_ignoreuntil,'(');
          .actualdepth++;
          data['depthincr']=true;
          .insidefunction=true;

          if watchingvariables then
            varstmp=trim(l);
            pos=index(varstmp,')');
            if pos # -1 then
              varstmp=trim(substr(varstmp,0,pos));
            end;
            pos=index(varstmp,'(');
            if pos # -1 then
              varstmp=trim(substr(varstmp,pos+1));
            end;
            varsarray=[];
            while varstmp # '' do
              pos=index(varstmp,',');
              if pos # -1 then
                vartmp=trim(substr(varstmp,0,pos));
                varstmp=trim(substr(varstmp,pos+1));
              else
                vartmp=varstmp;
                varstmp='';
              end;
              pos=index(vartmp,'=');
              if pos # -1 then
                vartmp=trim(substr(vartmp,0,pos));
              end;
              if vartmp # '' then
               append(varsarray,vartmp);
              end;
            end;
            for i=len(varsarray)-1 to 0 by -1 do
              SetNewVariable(varsarray[i]);
            end;
          end;
        in 'if':
          .actualdepth++;
          if IsntIgnored(string) then
            data['position']=prevpos;
            data['valid']=true;
            SetActiveVariables(data);
          end;
          append(.line_ignoreuntil,'then');
        in 'in':
          IsntIgnored(string);
     	    if len(.line_ignoreuntil) = 0 or .line_ignoreuntil[len(.line_ignoreuntil)-1] # 'do' then
                   //in 'case', not 'for'
            append(.line_ignoreuntil,':');
          end;
        in 'return':  //decr. before
          if data['depthdecr_in'] = null then
            data['depthdecr_in']=[];
          end;
          append(data['depthdecr_in'],len(line)-len(l)-len('return'));
          if IsntIgnored(string) then
            data['position']=prevpos;
            data['valid']=true;
          end;
        in 'try':
          .actualdepth++;
          IsntIgnored(string);
        in 'until':
          if IsntIgnored(string) then
            data['position']=prevpos;
            data['valid']=true;
            SetActiveVariables(data);
            .actualdepth--;
          end;
        in 'while':
          .actualdepth++;
          if IsntIgnored(string) then
            data['position']=prevpos;
            data['valid']=true;
            SetActiveVariables(data);
          end;
          append(.line_ignoreuntil,'do');
        else
          if IsntIgnored(string) then
            data['position']=prevpos;
            data['valid']=true;
            SetActiveVariables(data);
          end;
      end;
    end;
  end;

  return data;
end;

function VarAsString(variable)
  if isstr(variable) then
    return '\''+variable+'\'';
  elsif isarray(variable) and len(variable) = 0 then
    return 'empty array';
  else
    return str(variable);
  end;
end;

function ShowVariables(variables,where)
  if where = null then
    title='Variables';
  else
    title='Variable '+where;
  end;

  if isarray(variables) and len(variables) > 0 then
    listtext=[];
    wheres=[];
    varkeys=keys(variables);
    i=0;
    for variable in variables do
      if varkeys[i] = null then
        varkey=i;
      else
        varkey=VarAsString(varkeys[i]);
      end;

      if where = null then
        append(listtext,varkeys[i]+'='+VarAsString(variable));
        append(wheres,varkeys[i]);
      else
        append(listtext,varkey+':'+VarAsString(variable));
        append(wheres,where+'['+varkey+']');
      end;
      i++;
    end;
    choice=0;
    while true do
      choice=ui.list(listtext,false,[choice],title);
      if choice = null then
        break;
      end;
      choice=choice[0];
      if where = null or true then
        newwhere=wheres[choice];
      else
        newwhere=where+'['+VarAsString(wheres[choice])+']';
      end;

      ShowVariables(variables[choice],newwhere);
    end;
  else
    ui.msg(VarAsString(variables),title);
  end;
end;

function CreateDebugProgram(name,watchingvariables)
  infile=.thisprogram+'.m';
  outfile=.debugprogram+'.m';

  quickprepare=false;
  if files.exists(outfile) and files.time(outfile) > files.time(infile) then
    out=io.open(outfile);
    pos=io.size(out)-14-2;
    if pos >= 0 then
      io.seek(out,pos);
      if io.read(out,14) = '//KyblDebugEnd' then //correct end of file
        quickprepare=true;
      end;
    end;
    io.close(out);
  end;

  print 'Preparing '+name+'...';

  if not quickprepare then
    out=io.create(outfile,io.raw);
    io.write(out,char([239,187,191]));  //utf-8 signature
    io.ces(out,io.utf8);

    inp=io.open(infile,false,io.bom);
    io.read(inp,2);  // skip comment
    while true do
      line=io.readln(inp,1000);
      if line = '*/' then break; end;
      io.println(out,line);
    end;
    io.close(inp);
  end;

  inp=io.open(name+'.m',false,io.bom);
  linenum=0;
  fileposition=0;
  while true do
    line=io.readln(inp,1000);
    if line = null then break; end;
    if not quickprepare then
      actualdepthbefore=.actualdepth;
      data=GetValidPosOfLine(line,watchingvariables);
      linetowrite=line;

      if data['depthdecr_in'] # null then      //inserted returns
        for i=len(data['depthdecr_in'])-1 to 0 by -1 do
          linetowrite=substr(linetowrite,0,data['depthdecr_in'][i])+';.kybldebuggerfdepth--;'+substr(linetowrite,data['depthdecr_in'][i]);
        end;
      end;

      if data['valid'] then
        linetowrite=substr(linetowrite,0,data['position'])+';KyblDebugInfo('+linenum+','+actualdepthbefore+','+fileposition+',['+data['activevariables_line']+']);'+substr(linetowrite,data['position']);
      end;
      if data['depthdecr'] # null then
        io.write(out,';.kybldebuggerfdepth--;');
      end;

      io.writeln(out,linetowrite);

      if data['depthincr'] # null then
        io.write(out,';.kybldebuggerfdepth++;');
      end;
      fileposition+=len(line)+1; //+1=enter
      linenum++;
    end;

    append(.kybldebuglines,line);
  end;

  if not quickprepare then
    io.writeln(out,';KyblDebugInfo(\'quit\','+.actualdepth+');');

    if .iscomment or len(.line_ignoreuntil) # 0 then
      if not ui.confirm('Parsing error. Continue?') then
        return false;
      end;
    else
      io.writeln(out,'//KyblDebugEnd');
    end;

    io.close(out);
  end;
  io.close(inp);

  return true;
end;

function DebugProgram(name,watchingvariables)
  .kybldebuglines=[];
  .actualdepth=0;
  .insidefunction=false;
  .line_ignoreuntil=[];
  .iscomment=false;
  .activevariables=[];

  .debugprogram=name+'-debug';
  try
    if proc.runs(.debugprogram) then
      proc.stop(.debugprogram);
      proc.close(.debugprogram);
    end;
  catch e by  //ok
  end;

  if not CreateDebugProgram(name,watchingvariables) then
    return;
  end;

  pipein=proc.pipe('KyblDebuggerIn');
  pipeout=proc.pipe('KyblDebuggerOut');

  if io.avail(pipein) # 0 then
           //some waste in pipe
    io.read(pipein,io.avail(pipein));
  end;

  print 'Debugging...';

  proc.run(.debugprogram);

  stopdebug=false;
  goedit=false;
  wait=50;
  while not stopdebug do
    if io.avail(pipein) > 0 then
      wait=50;
      if .displayeddebugapp then
        proc.show(.thisprogram);
        .displayeddebugapp=false;
      end;

      msg=io.readm(pipein);
      case msg[0]
        in 'breakpoint':
          linenum=msg[1];
          depth=msg[2];
          fileposition=msg[3];

          if io.avail(pipein) # 0 then
print 'waste in pipe #1';
            io.read(pipein,io.avail(pipein));
          end;

          while true do
            response=Breakpoint(linenum,depth);
            if response = 'showoutput' then
              io.writem(pipeout,['showoutput']);
              proc.show(.debugprogram);
              reply=io.readm(pipein);
              if reply[0] # 'ok' then
                throw 'Unrecognized reply ('+reply+')';
              end;
              proc.show(.thisprogram);
            elsif response = 'showvariables' then
              if not watchingvariables then
                ui.error('You don\'t allowed variables watching during start of debugging.');
              else
                io.writem(pipeout,['showvariables']);
                reply=io.readm(pipein);
                if reply[0] # 'variablesstart' then
                  throw 'Unrecognized reply ('+reply+')';
                end;
                if reply[1] = 0 then
                  ui.msg('No active variables yet.','Note');
                else
                  variables=[];
                  for count=reply[1]-1 to 0 by -1 do
                    reply=io.readm(pipein);
                    if reply[0] # 'variable' then
                      throw 'Unrecognized reply '+reply;
                    end;
                    variables[reply[1]]= reply[2];
                  end;
                  ShowVariables(variables,null);
                end;
              end;
            elsif response = 'editfile' then
              if ui.confirm('Exit debugger and start edit '+name+' at line '+(linenum+1)+'?','Go to editor') then
                stopdebug=true;
                goedit=fileposition;
                break;
              end;
            elsif response = 'quit' then
              stopdebug=true;
              break;
            else
              io.writem(pipeout,[response]);
              break;
            end;
          end;
        in 'quit': break;
        else print msg; throw msg;
      end;
    else
      if not proc.runs(.debugprogram) then
        ui.msg('Program terminated.');
        stopdebug=true;
      else
        sleep(wait);
        wait=wait*1.5;
        if wait > 300 then
          wait=300;
          if not .displayeddebugapp then
            proc.show(.debugprogram);
            .displayeddebugapp=true;
          end;
        end;
      end;
    end;
  end;

  if io.avail(pipein) # 0 then
           //some waste in pipe
print 'waste in pipe #2';
    io.read(pipein,io.avail(pipein));
  end;

  io.close(pipein);
  io.close(pipeout);


  try
    proc.stop(.debugprogram);
  catch e by end;
  try
    proc.close(.debugprogram);
  catch e by end;

  graph.hide();
  .programstoremove[.debugprogram]=true;
  print 'Program finished.\n';


  if .displayeddebugapp then
    proc.show(.thisprogram);
    .displayeddebugapp=false;
  end;

  return goedit;
end;

function ChooseProgramToDebug()
  apps=files.scan('*.m',0,0);
  newapps=[];
  for app in apps do
    if len(app) < 8 or substr(app,len(app)-8) # '-debug.m' then
      append(newapps,substr(app,0,len(app)-2));
    end;
  end;
  ret=ui.form(['Choose action or program to debug','Program to debug': array.concat(.lastused,['<Settings>','<About>'],newapps),'Variables watching (slower)': .watchingvariables],'KýblDebugger Menu');
  if ret = null then
    return null;
  else
    return ['program': ret[0],'watchingvariables': ret[1]];
  end;
end;

cls();
print 'Welcome in KýblDebugger v'+.debuggerversion+'\n';

.lastused=[];

if not LoadSettings() then
  print 'This is the first start of KýblDebugger. It\'s needed to set keys.\nPlease press any key to continue.\n';
  ui.keys(false);
  ui.cmd();
  SetKeys();

  print 'Setting was saved to '+.thisprogram+'.cfg. Now you can choose program to debugging.\nPlease press any key to continue.\n';
  SaveSettings();

  ui.keys(false);
  ui.cmd();
end;

while true do
  program=ChooseProgramToDebug();
  if program = null then
    print 'Bye!';
    .programstoremove=keys(.programstoremove);
    for program in .programstoremove do
      try
        files.delete(program+'.m');
      catch e by
      end;
    end;
    break;
  elsif program['program'] = '<Settings>' then
    SetKeys();
    SaveSettings();
  elsif program['program'] = '<About>' then
    ui.msg('KýblDebugger v'+.debuggerversion+'\nThe first debugger for m-shell\n\nNote: Not all syntaxes are supported yet. If you have problems with executing, make sure that all each commands are just in one line.\n\n(C) Aleš Janda, KÝBLSoft 2008\nwww.kyblsoft.cz/mobil');
  elsif not files.exists(program['program']+'.m') then
    ui.error('File '+program['program']+'.m not found!');
  else
    .watchingvariables=program['watchingvariables'];

    index=array.index(.lastused,program['program']);
    if index # -1 then
      array.remove(.lastused,index);
    end;
    array.insert(.lastused,0,program['program']);
    if len(.lastused) > .maxlastused then
      array.remove(.lastused,.maxlastused,len(.lastused)-.maxlastused);
    end;

    SaveSettings();
    goedit=DebugProgram(program['program'],program['watchingvariables']);
    if goedit # false then
      print 'Going to editor...';
      files.edit(program['program']+'.m',goedit);
     break;
    end;
  end;

  sleep(100);  //while parse error, next dialog isn't shown without this delay (probably m-shell bug)
end;

proc.close();

