commit 2edd646b5bceb92bcd548d761c35d69b3b823654 Author: Pavel Shevaev Date: Mon May 16 14:23:26 2022 +0300 first commit diff --git a/README.md b/README.md new file mode 100644 index 0000000..e69de29 diff --git a/helpers.inc.php b/helpers.inc.php new file mode 100644 index 0000000..2ef070c --- /dev/null +++ b/helpers.inc.php @@ -0,0 +1,1125 @@ + $cur) + { + if(($mode == 1 && !$cur->isDir()) || + ($mode == 2 && $cur->isDir())) + { + if(!$only_extensions) + $files[] = $filename; + else + { + $flen = strlen($filename); + foreach($only_extensions as $ext) + { + if(substr_compare($filename, $ext, $flen-strlen($ext)) === 0) + $files[] = $filename; + } + } + } + } + } + return $files; +} + +function normalize_path($path, $unix=null/*null means try to guess*/) +{ + if(is_null($unix)) + $unix = !is_win(); + + $path = str_replace('\\', '/', $path); + $path = preg_replace('/\/+/', '/', $path); + $parts = explode('/', $path); + $absolutes = array(); + foreach($parts as $part) + { + if('.' == $part) + continue; + + if('..' == $part) + array_pop($absolutes); + else + $absolutes[] = $part; + } + $res = implode($unix ? '/' : '\\', $absolutes); + return $res; +} + +function normalize_sed_path($path) +{ + $path = normalize_path($path); + if(is_win()) + $path = str_replace('\\', '\\\\', $path); + return $path; +} + +//NOTE: Escaping multibyte unicode chars as \uXXXX impacts decoding performance drastically +//so we unescape them while encoding json +function json_encode_unescaped($arr) +{ + array_walk_recursive($arr, function (&$item, $key) { if (is_string($item)) $item = mb_encode_numericentity($item, array (0x80, 0xffff, 0, 0xffff), 'UTF-8'); }); + return mb_decode_numericentity(json_encode($arr), array (0x80, 0xffff, 0, 0xffff), 'UTF-8'); +} + +function json_make_pretty($json) +{ + return prettyJSON($json); +} + +function need_to_regen($file, array $deps, $debug = false) +{ + if(!is_file($file)) + { + if($debug) + echo "! $file\n"; + return true; + } + + $fmtime = filemtime($file); + foreach($deps as $dep) + { + if(is_file($dep) && (filemtime($dep) > $fmtime)) + { + if($debug) + echo "$dep > $file\n"; + return true; + } + } + + return false; +} + +function need_to_regen_any(array $files, array $deps, $debug = false) +{ + $earliest_file = null; + $earliest_time = 2e32; + + foreach($files as $file) + { + if(!is_file($file)) + return true; + + $time = filemtime($file); + if($time < $earliest_time) + { + $earliest_file = $file; + $earliest_time = $time; + } + } + + if($debug) + { + $date = date(DATE_RFC2822, $earliest_time); + echo "need_to_regen_any, earliest file: $earliest_file ($date)\n"; + } + + return need_to_regen($earliest_file, $deps, $debug); +} + +function fnmatch_patterns($file, array $fnmatch_patterns) +{ + foreach($fnmatch_patterns as $pattern) + { + if(fnmatch($pattern, $file)) + { + return true; + } + } + + return false; +} + +//force mode: +//0 - none, +//1 - force write always, +//2 - write only if content differs +//3 - write only if content differs, don't even touch if they are same +function gen_file($tpl_file, $result_file, $deps = array(), $force = 0, $perms = 0640) +{ + global $GAME_ROOT; + + $deps[] = $tpl_file; + if($force > 0 || need_to_regen($result_file, $deps)) + { + $txt = file_get_contents($tpl_file); + if($txt === false) + throw new Exception("Bad template settings file $tpl_file"); + + //replacing %(FOO)% alike entries with taskman config values + $out = _($txt); + + if(($force == 2 || $force == 3) && is_file($result_file)) + { + $prev = file_get_contents($result_file); + if($prev === false) + throw new Exception("Could not read file $result_file"); + + //if contents is similar no need to write it + if(strcmp($prev, $out) === 0) + { + if($force == 2) + touch($result_file); + return; + } + } + + ensure_write($result_file, $out); + chmod($result_file, $perms); + } +} + +function ensure_read($file) +{ + $c = file_get_contents($file); + if($c === false) + throw new Exception("Could not read file '$file'"); + return $c; +} + +function ensure_write($dst, $txt, $dir_perms = 0777, $flags = 0) +{ + $dir = dirname($dst); + + if(!is_dir($dir)) + mkdir($dir, $dir_perms, true); + + msg_dbg("> $dst ...\n"); + if(file_put_contents($dst, $txt, $flags) === false) + throw new Exception("Could not write to '$dst'"); +} + +function ensure_write_if_differs($dst, $txt, $dir_perms = 0777, $flags = 0) +{ + if(is_file($dst) && file_get_contents($dst) == $txt) + return; + + ensure_write($dst, $txt, $dir_perms, $flags); +} + +const COPY_MODE_BUILTIN = 1; +const COPY_MODE_SYSTEM = 2; +const COPY_MODE_HARDLINK = 3; + +function ensure_copy($src, $dst, $dir_perms = 0777, $excludes = array()) +{ + recurse_copy($src, $dst, $dir_perms, COPY_MODE_BUILTIN, false, $excludes); +} + +function ensure_copy_file_if_differs($src_file, $dst_file, $dir_perms = 0777) +{ + if(!is_file($dst_file) || filesize($src_file) != filesize($dst_file) || crc32_file($src_file) !== crc32_file($dst_file)) + ensure_copy($src_file, $dst_file, $dir_perms); +} + +function ensure_sync($src, $dst, $dir_perms = 0777, $excludes = array()) +{ + recurse_copy($src, $dst, $dir_perms, COPY_MODE_BUILTIN, true, $excludes); +} + +function ensure_hardlink($src, $dst, $dir_perms = 0777, $excludes = array()) +{ + recurse_copy($src, $dst, $dir_perms, COPY_MODE_HARDLINK, true, $excludes); +} + +function ensure_duplicate($src, $dst, $dir_perms = 0777) +{ + recurse_copy($src, $dst, $dir_perms, COPY_MODE_SYSTEM); +} + +function recurse_copy($src, $dst, $dir_perms = 0777, $copy_mode = COPY_MODE_BUILTIN, $mtime_check = false, $excludes = array()) +{ + msg_dbg("copying $src => $dst ...\n"); + + if(!is_file($src) && !is_dir($src)) + throw new Exception("Bad file or dir '$src'"); + + foreach($excludes as $exclude_pattern) + { + if(preg_match("~$exclude_pattern~", $src)) + return; + } + + if(!is_dir($src)) + { + _ensure_copy_file($src, $dst, $copy_mode, $mtime_check); + return; + } + + $dir = opendir($src); + ensure_mkdir($dst, $dir_perms); + while(false !== ($file = readdir($dir))) + { + if(($file != '.' ) && ($file != '..')) + { + if(is_dir($src . '/' . $file)) + recurse_copy($src . '/' . $file, $dst . '/' . $file, $dir_perms, $copy_mode, $mtime_check, $excludes); + else + { + $excluded = false; + foreach($excludes as $exclude_pattern) + $excluded = $excluded || (bool)preg_match("~$exclude_pattern~", $src . '/' . $file); + + if($excluded) + continue; + + _ensure_copy_file($src . '/' . $file, $dst . '/' . $file, $copy_mode, $mtime_check); + } + } + } + closedir($dir); +} + +function _ensure_copy_file($src, $dst, $copy_mode = COPY_MODE_BUILTIN, $mtime_check = false) +{ + if($mtime_check && file_exists($dst) && filemtime($src) <= filemtime($dst)) + return; + + msg_dbg("copy ($copy_mode): $src => $dst\n"); + ensure_mkdir(dirname($dst)); + + if($copy_mode == COPY_MODE_SYSTEM) + shell("cp -a $src $dst"); + else if($copy_mode == COPY_MODE_BUILTIN) + { + if(!copy($src, $dst)) + throw new Exception("Could not copy '$src' to '$dst'"); + } + + else if($copy_mode == COPY_MODE_HARDLINK) + { + if(!link($src, $dst)) + throw new Exception("Could make a hard link '$src' to '$dst'"); + } + else + throw new Exception("Unrecognized copy mode $copy_mode"); +} + +function ensure_identical($src, $dst, $excludes = array()) +{ + msg_dbg("deleting files missing in $src from $dst ...\n"); + + if(!is_dir($dst) && !is_file($dst)) + throw new Exception("dst '$dst' must be a valid file or dir!"); + + foreach($excludes as $exclude_pattern) + { + if(preg_match("~$exclude_pattern~", $src)) + return; + } + + $dir = opendir($dst); + while(false !== ($file = readdir($dir))) + { + if(($file != '.' ) && ($file != '..')) + { + $src_file = $src . '/' . $file; + $dst_file = $dst . '/' . $file; + + if(is_dir($src_file)) + ensure_identical($src_file, $dst_file, $excludes); + else + { + $excluded = false; + foreach($excludes as $exclude_pattern) + { + $excluded = $excluded || (bool)preg_match("~$exclude_pattern~", $src_file); + } + + if($excluded) + continue; + + if(is_file($dst_file) && !is_file($src_file)) + ensure_rm($dst_file); + } + } + } + closedir($dir); +} + +function ensure_symlink($src, $dst, $dir_perms = 0777) +{ + if(!is_file($src) && !is_dir($src)) + throw new Exception("Bad file or dir '$src'"); + + $dir = dirname($dst); + if(!is_dir($dir)) + mkdir($dir, $dir_perms, true); + + ensure_rm($dst); + + msg_dbg("symlinking $src -> $dst \n"); + if(!symlink($src, $dst)) + throw new Exception("Could not create symlink"); +} + +function ensure_rm($what) +{ + if(is_dir($what) && !is_link($what)) + rrmdir($what); + else if(is_file($what) || is_link($what)) + unlink($what); +} + +function ensure_mkdir($dir, $perms = 0775) +{ + if(is_dir($dir)) + return; + + msg_dbg("mkdir $dir\n"); + if(!mkdir($dir, $perms, true)) + throw new Exception("Could not create dir '$dir'"); + + msg_dbg("chmod " . decoct($perms) . " $dir\n"); + if(!chmod($dir, $perms)) + throw new Exception("Could not chmod " . decoct($perms) . " dir '$dir'"); +} + +function ensure_var_dir($dir) +{ + ensure_mkdir($dir, 0777); + $items = fmatch("$dir/*"); + foreach($items as $item) + { + $perms = is_dir($item) ? 0770 : 0660; + + msg_dbg("chmod " . decoct($perms) . " $item\n"); + if(!chmod($item, $perms)) + throw new Exception("Could not chmod " . decoct($perms) . " '$item'"); + } +} + +function compare_files_timestamp($file_a, $file_b) +{ + $file_a_timestamp = file_exists($file_a) ? filemtime($file_a) : 0; + $file_b_timestamp = file_exists($file_b) ? filemtime($file_b) : 0; + if($file_a_timestamp >= $file_b_timestamp) + return 1; + else + return -1; +} + +function fmatch($pat) +{ + $res = glob($pat); + if(!is_array($res)) + return array(); + return $res; +} + +function rrmdir($dir, $remove_top_dir = true) +{ + if(is_dir($dir)) + { + $objects = scandir($dir); + foreach($objects as $object) + { + if($object != "." && $object != "..") + { + if(filetype($dir."/".$object) == "dir") + rrmdir($dir."/".$object); + else + unlink($dir."/".$object); + } + } + } + + if($remove_top_dir) + { + if(is_link($dir)) + unlink($dir); + else if(is_dir($dir)) + rmdir($dir); + } +} + +function run_apple_script($script) +{ + $vm = popen("osascript", "w"); + fwrite($vm, $script); + // run script will always exit with status 1 + pclose($vm); +} + +function client_xcode_build($scheme, $config = 'Debug', $sdk = 'iphonesimulator4.3') +{ + global $GAME_ROOT; + + $xcode_filter = ". $GAME_ROOT/utils/xcodefilter.sh"; + + ensure_mkdir("$GAME_ROOT/build/client"); + shell(_( + "cd $GAME_ROOT/client && " . + "$xcode_filter xcodebuild -workspace %XCODE_WKSPACE% -scheme $scheme -configuration $config -sdk $sdk SYMROOT=$GAME_ROOT/build/client" + )); +} + +function task_git_info() +{ + list($rev_hash, $branch, $revision_number) = git_get_info(); + $info = "==============GIT INFO==============\n" + . "$rev_hash - hash\n" + . "$branch - branch\n" + . "$revision_number - revision\n"; + echo $info; +} + +function git_get_info() +{ + global $GAME_ROOT; + + if(!is_dir("$GAME_ROOT/.git")) + throw new Exception("Not a Git repository"); + + $out = array(); + exec("git rev-parse HEAD", $out); + $rev_hash = trim($out[0]); + if(!$rev_hash) + throw new Exception("Error getting git revision hash"); + + $out = array(); + exec("git rev-parse --abbrev-ref HEAD", $out); + $branch = trim($out[0]); + if(!$branch) + throw new Exception("Error getting git branch"); + + $out = array(); + exec("git rev-list HEAD --count", $out); + $revision_number = (int)$out[0]; + if(!$revision_number) + throw new Exception("Error getting git revision number"); + + return array($rev_hash, $branch, $revision_number); +} + +function git_get_rev_hash() +{ + list($rev_hash, $_, $__) = git_get_info(); + return $rev_hash; +} + +function git_get_branch() +{ + list($_, $branch, $__) = git_get_info(); + return $branch; +} + +function git_get_rev_number() +{ + list($_, $__, $rev_number) = git_get_info(); + return $rev_number; +} + +function git_try_commit($files, $msg) +{ + try + { + exec("git add $files && git commit -m \"$msg\" && git pull && git push"); + } + catch(Exception $e) + { + echo "Failed to commit changes. Apparently, nothing changed\n"; + } +} + +function decode_json_ensure($json, $assoc = false) +{ + $res = json_decode($json, $assoc); + if($res === null) + { + $parser = new JsonParser(); + $err = $parser->lint($json); + echo $json; + throw $err; + } + return $res; +} + +function convert_js_to_msgpack($file, $bin_file) +{ + $bin_file = "$file.lib"; + if(need_to_regen($bin_file, array($file))) + { + echo "Converting $file -> $bin_file...\n"; + $data = decode_json_ensure(file_get_contents($file)); + $msg = config_msgpack_pack($data); + ensure_write($bin_file, $msg); + } + return true; +} + +function check_and_decode_json($json) +{ + try + { + $arr = decode_json_ensure($json, true); + return array(0, "", $arr); + } + catch(Exception $e) + { + return array(1, $e->getMessage(), array()); + } +} + +function check_and_decode_jzon($json) +{ + try + { + $arr = jzon_parse($json); + return array(0, "", $arr); + } + catch(Exception $e) + { + return array(1, $e->getMessage(), array()); + } +} + +function make_file_md5($file, $md5_file) +{ + if(!need_to_regen($md5_file, array($file))) + return file_get_contents($md5_file); + + $md5 = md5_file($file); + ensure_write($md5_file, $md5); + + return $md5; +} + +function make_dir_md5($dir, $md5_file) +{ + $files = scan_files_rec(array($dir)); + + if(!need_to_regen($md5_file, $files)) + return; + + $md5s = array(); + foreach($files as $file) + $md5s[] = md5_file($file); + + $md5 = md5(implode('', $md5s)); + ensure_write($md5_file, $md5); +} + +function file_put_contents_atomic($filename, $content, $mode = 0644) +{ + $temp = tempnam(dirname($filename), 'atomic'); + if(!($f = @fopen($temp, 'wb'))) + throw new Exception("Error writing temporary file '$temp'"); + + fwrite($f, $content); + fclose($f); + + if(!@rename($temp, $filename)) + { + @unlink($filename); + @rename($temp, $filename); + } + chmod($filename, $mode); +} + +function gmgetdate($ts = null) +{ + $k = array('seconds','minutes','hours','mday', + 'wday','mon','year','yday','weekday','month',0); + return(array_combine($k, explode(":", + gmdate('s:i:G:j:w:n:Y:z:l:F:U',is_null($ts)?time():$ts)))); +} + +function prettyJSON($json) +{ + $result = ''; + $level = 0; + $prev_char = ''; + $in_quotes = false; + $ends_line_level = NULL; + $json_length = strlen($json); + + for($i = 0; $i < $json_length; $i++) + { + $char = $json[$i]; + $new_line_level = NULL; + $post = ""; + if($ends_line_level !== NULL) + { + $new_line_level = $ends_line_level; + $ends_line_level = NULL; + } + if($char === '"' && $prev_char != '\\') + { + $in_quotes = !$in_quotes; + } + else if(!$in_quotes) + { + switch($char) + { + case '}': case ']': + $level--; + $ends_line_level = NULL; + $new_line_level = $level; + break; + case '{': case '[': + $level++; + case ',': + $ends_line_level = $level; + break; + case ':': + $post = " "; + break; + case " ": case "\t": case "\n": case "\r": + $char = ""; + $ends_line_level = $new_line_level; + $new_line_level = NULL; + break; + } + } + if($new_line_level !== NULL) + { + $result .= "\n".str_repeat(" ", $new_line_level); + } + $result .= $char.$post; + $prev_char = $char; + } + + $result = str_replace('"<%', '<%', $result); + $result = str_replace('%>"', '%>', $result); + $result = str_replace('\"', '"', $result); + $result = str_replace('\\\\', '\\', $result); + $result = str_replace('\/', '/', $result); + $result = str_replace('"{', '{', $result); + $result = str_replace('}"', '}', $result); + + return $result."\n"; +} + +function crc32_file($file) +{ + $hash = hash_file('crc32b', $file); + $array = unpack('N', pack('H*', $hash)); + return $array[1]; +} + +function run_shell_in_project_dir($cmd) +{ + global $GAME_ROOT; + + shell("cd $GAME_ROOT && $cmd", $out); + return $out[0]; +} + +function boolstr($b) +{ + return $b ? 'true' : 'false'; +} + +function create_js_template_literal($str) +{ + $result = str_replace('\\', '\\\\', $str); // replace \ -> \\ + $result = str_replace('`', '\\`', $result); // replace ` -> \` + $result = str_replace('$', '\\$', $result); // replace $ -> \$ + return "`$result`"; +} + +function create_go_string_literal($str) +{ + $result = str_replace('\\', '\\\\', $str); // replace \ -> \\ + $result = str_replace('"', '\\"', $result); // replace " -> \" + $result = str_replace("\n", '\\n', $result); // replace new_line -> \n + return '"' . $result . '"'; +} + +function convertFileFromDos2UnixFormat($file) +{ + $file_tmp = $file.".tmp"; + + if(is_win()) + replace_text_in_file("\\r", "", $file, $file_tmp); + else + shell("sed \$'s/\\r\$//' $file > $file_tmp"); + + ensure_copy($file_tmp, $file); + ensure_rm($file_tmp); +} + +function removeCarriageReturn($file) +{ + $file_tmp = $file.".tmp"; + + if(is_win()) + replace_text_in_file("\\\\r", "", $file, $file_tmp); + else + shell("sed 's/\\\\r//' $file > $file_tmp"); + + ensure_copy($file_tmp, $file); + ensure_rm($file_tmp); +} + +function replace_text_in_file($search_pattern, $replace_pattern, $from_file, $to_file) +{ + $file_content = ensure_read($from_file); + $res = str_replace($search_pattern, $replace_pattern, $file_content); + ensure_write($to_file, $res); +} + +function gamectl_get_props_as_php_code() +{ + $props = props(); + $props_str = " $v) + $props_str .= "taskman_propset('$k', " . var_export($v, true). ");\n"; + return $props_str; +} + +function run_background_proc($bin, array $args = array(), $redirect_out = '', $redirect_err = '') +{ + if(is_win()) + { + //TODO: migrate to background jobs + $cmd = "powershell.exe Start-Process $bin -NoNewWindow"; + if($args) + { + $cmd .= ' -ArgumentList '; + foreach($args as $arg) + $cmd .= "'$arg',"; + $cmd = rtrim($cmd, ','); + } + if($redirect_out) + $cmd .= " -RedirectStandardOutput '$redirect_out'"; + if($redirect_err) + $cmd .= " -RedirectStandardError '$redirect_err'"; + pclose(popen($cmd, 'r')); + } + else + { + $cmd = $bin; + foreach($args as $arg) + $cmd .= ' ' . escapeshellarg($arg); + if($redirect_out) + $cmd .= ' > ' . escapeshellarg($redirect_out); + if($redirect_err) + $cmd .= ' 2> ' . escapeshellarg($redirect_err); + $cmd .= ' &'; + exec($cmd, $_, $ret); + if($ret !== 0) + throw new Exception("Error starting worker: $cmd ($ret)"); + } +} + +function run_background_gamectl_workers($task, array $worker_args) +{ + global $GAME_ROOT; + + $results = array(); + $workers = array(); + + foreach($worker_args as $idx => $args) + { + $uid = uniqid(); + $in_file = $GAME_ROOT . "/build/tmp/in_$uid.work"; + $out_file = $GAME_ROOT . "/build/tmp/out_$uid.work"; + $log_file = $GAME_ROOT . "/build/tmp/log_$uid.work"; + $err_file = $GAME_ROOT . "/build/tmp/err_$uid.work"; + + $workers[] = array($in_file, $out_file, $log_file, $err_file); + + ensure_write($in_file, serialize($args)); + + $cwd = getcwd(); + chdir($GAME_ROOT); + run_background_proc( + is_win() ? 'gamectl.bat' : './gamectl', + array('-b', $task, $in_file, $out_file, $err_file), + $log_file, $err_file + ); + chdir($cwd); + } + + try + { + $log_handles = array(); + while(sizeof($results) < sizeof($workers)) + { + sleep(1); + clearstatcache(); + + foreach($workers as $idx => $worker) + { + if(isset($results[$idx])) + continue; + + list($in_file, $out_file, $log_file, $err_file) = $worker; + + if(!isset($log_handles[$idx]) && is_file($log_file)) + $log_handles[$idx] = fopen($log_file, 'r'); + + if(isset($log_handles[$idx])) + { + while(($buffer = fgets($log_handles[$idx])) !== false) + echo $buffer; + + $pos = ftell($log_handles[$idx]); + fclose($log_handles[$idx]); + $log_handles[$idx] = fopen($log_file, "r"); + fseek($log_handles[$idx], $pos); + } + + if(is_file($err_file) && filesize($err_file) > 0) + throw new Exception("Error in worker $idx:\n" . file_get_contents($err_file)); + + if(is_file($out_file)) + $results[$idx] = @unserialize(ensure_read($out_file)); + } + } + } + finally + { + foreach($workers as $item) + { + list($in_file, $log_file, $err_file) = $item; + @ensure_rm($in_file); + @ensure_rm($out_file); + @ensure_rm($log_file); + @ensure_rm($err_file); + } + } + + return $results; +} + +function kb($str) +{ + return kb_len(strlen($str)); +} + +function kb_len($len) +{ + return round($len/1024,2) . "kb"; +} + +function gen_uuid_v4() +{ + $UUID_LENGTH_BYTES = 16; + $data = random_bytes($UUID_LENGTH_BYTES); + + $data[6] = chr(ord($data[6]) & 0x0f | 0x40); // set version to 0100 + $data[8] = chr(ord($data[8]) & 0x3f | 0x80); // set bits 6-7 to 10 + + return vsprintf('%s%s-%s-%s-%s-%s%s%s', str_split(bin2hex($data), 4)); +} + +function watch_running_process($pid, $log_file, $exit_matches = array(), $error_matches = array(), $verbose = true, $break_after_sec = -1, $ignored_errors = array(), $noted_warnings = array()) +{ + $matches_any_fn = function($buffer, array $matches) + { + foreach($matches as $match) + { + if(strpos($buffer, $match) !== false) + return true; + } + return false; + }; + + $h = fopen($log_file, "r"); + $start = time(); + $warnings_list = array(); + + $throw_fn = function($msg) use (&$warnings_list) + { + if(count($warnings_list) > 0) + $msg .= "\n\nPossible causes:\n" . implode("\n", $warnings_list) . "\n"; + throw new Exception($msg); + }; + + $process_gone = false; + + while(true) + { + $buffer = fgets($h); + + if($buffer !== false) + { + if($verbose) + echo $buffer; + + //log success condition + if($matches_any_fn($buffer, $exit_matches)) + break; + + if($matches_any_fn($buffer, $noted_warnings)) + $warnings_list[] = $buffer; + + if($matches_any_fn($buffer, $error_matches)) + { + if($matches_any_fn($buffer, $ignored_errors)) + echo "Error in log file IGNORED\n"; + else + $throw_fn("Error condition in log file detected"); + } + } + else if($process_gone) + $throw_fn("Process with id: '$pid' not found"); + else + { + usleep(200000); + $pos = ftell($h); + fclose($h); + $h = fopen($log_file, "r"); + fseek($h, $pos); + + //check if process still exists + if(check_process($pid) !== 0) + { + $process_gone = true; + continue; + } + } + + if($break_after_sec > 0 && (time() - $start) > $break_after_sec) + break; + + } + fclose($h); +} + +function check_process($pid) +{ + if(is_win()) + { + $cmd = "powershell Get-Process -Id $pid"; + } + else + { + $cmd = "ps -p $pid"; + } + + exec($cmd, $_, $ret); + return $ret; +} + +function which_dir($bin) +{ + return dirname(which_path($bin)); +} + +function which_path($bin) +{ + if(is_win()) + { + shell("where $bin", $out); + return trim($out[0]); + } + else + { + shell("which $bin", $out); + return trim($out[0]); + } +} + +function arg_exists($args, $needle) +{ + $strict = true; + return in_array($needle, $args, $strict); +} +