function err(msg, line) {
  if(line != 646) {
    graph("/tmp/error.dot")
    error(msg + ". See /tmp/error.dot")
  } else print(msg)
}

const ALLOC = 0
const ISPARAM = 1
const EXTERNAL = 2

function uno_check(vars, state, line) {
  if (vars.length == 1 && vars[0].isZero) {
    select();
    unmark(ALLOC);
  } else if (select("", [PARAM])) {
    mark(ISPARAM)
  } else if (select("malloc", [FCALL]))    // unmarked symbols of type function call
  {
    if (select("", [DEF]))    // unmarked symbols DEFined in those stmnts
    {
      if (match(ALLOC, [DEF]))  // are there matching symbols with mark ALLOC?
        err("malloc follows malloc", line)
      else {
        mark(ALLOC)    // mark ALLOC
        unmark(EXTERNAL)
      }
    } else
      err("result of malloc unused")
  } else if (select("", [PARAM_OF("free")]))
  {
    if (match(ALLOC) || match(EXTERNAL)) { // unlike uno we do not do a DFS, just statement iteration
      print("Freeing " + _selected[0].name)
      unmark()   // remove mark
    }
    else {
      list()
      err("free without malloc")
    }
  } else if (select("", [RETURN]))
  {
    // if a function returns a malloced value it's not a leak
    if (match(ALLOC)) {
      unmark()   // remove mark
    }
  } else if (select("", [FCALL])) {
    if(select("", [USE, ALIAS], [DEF])) {
      mark(EXTERNAL)
      print(_selected[0].name + " is external");
    }
  }
}

function path_end(state) {
  if (marked(ALLOC))
  {
    const alloc_len = _selected.length
    //if the allocated vars match escaping vars, we are good
    if(match(ISPARAM)) {
      if(alloc_len == _selected.length)
        return
    }
    if(!known_zero()) {
      list()
      err("malloc without free")
    }
  } // print("finished")
}
/*
void
uno_check(void)
{ // warning, named args to select() are currently not supported 

  if (select("malloc", FCALL))    // unmarked symbols of type function call
  { if (select("", DEF))    // unmarked symbols DEFined in those stmnts
    { if (match(ALLOC, DEF))  // are there matching symbols with mark ALLOC?
        err("malloc follows malloc")
      else
        mark(ALLOC)    // mark ALLOC
    } else
      err("result of malloc unused")
  } else

  if (select("free", FCALL))
  { if (select("", USE))
    { if (match(ALLOC, DEF))
        unmark()   // remove mark
      else
        err("free without malloc")
    } else
      err("no argument to free")
  }

  if (path_ends())
  { if (marked(ALLOC, ANY))
    { if (known_zero())
        no_error()
      else
        err("malloc without free")
  } }
}

*/