<?php if (!defined('PmWiki')) exit();
/**
  ZCode for PmWiki
  Highlight source code files extracted from tar or zip archives.
  Written by (c) 2017 Petko Yotov http://www.pmwiki.org/Petko

  This text is written for PmWiki; you can redistribute it and/or modify
  it under the terms of the GNU General Public License as published
  by the Free Software Foundation; either version 3 of the License, or
  (at your option) any later version. See pmwiki.php for full details
  and lack of warranty.
*/
# Version date
$RecipeInfo['ZCode']['Version'] = '20170812a';

SDVA($ZCode, array(
  'Archives' => 'archives/file-%s.tgz',
  'DefaultVersion' => 'latest',
  'VersionChars' => '-\\w.',
  'FilenameChars' => '-\\w.\\/',
  'FilenameExts' => 'php|tmpl|txt|css|htaccess|js|html?',
  'SingleLinePad' => 4,
));
SDVA($ZCode['Langs'], array('tmpl'=>'html', 'htaccess'=>'apache'));
SDVA($ZCode['ExtCommands'], array(
  'tgz|tar|tar\\.gz' => 'tar -f "%s" --wildcards --get "*%s" --to-stdout', 
  'zip'=>'unzip -p "%s" "*%s"', 
  'rar'=>'unrar p -inul "%s" "*%s"', 
  '7z'=>'7z e -so -r "%s"  "*%s" 2> /dev/null', 
));

Markup('(:zcode', 'directives', '/\\(:zcode +(.*?):\\)/', 'FmtZCode');

function FmtZCode($n) {
  global $ZCode, $HTMLHeaderFmt, $HTMLFooterFmt;
  $opt = ParseArgs($n[1], '(?>(\\w+)[=])');
  if(!@$opt['file']) {
    if(preg_match("!^(?:(.*?):)?(.*?)(?:@(\\d.*))?$!", $opt[""][0], $n)) {
      list($x, $opt['version'], $opt['file'], $opt['lines']) = $n;
    }
  }
  
  $fname = preg_replace('![^'.$ZCode['FilenameChars'].']!', '', $opt['file']);
  $fname = preg_replace('!\\.{2,}!', '', $fname); # no double dots in file names
  $ext = strtolower(preg_replace('!^.*\\.(\\w+)$!', '$1', $fname));
  
  if ( (!$fname) || ! preg_match("!^({$ZCode['FilenameExts']})$!i", $ext) ) 
    return XL("Unsupported file name:"). " '{$opt['file']}'";
  
  $version = ($opt['version']) ? $opt['version'] : $ZCode['DefaultVersion'];
  $version = preg_replace('![^'.$ZCode['VersionChars'].']!', '', $version);
  
  $archive = sprintf($ZCode['Archives'], $version);
  if (! file_exists($archive)) return XL("No such file:") ." '$archive'";
  
  $command = '';
  foreach($ZCode['ExtCommands'] as $e => $c) {
    if(! preg_match('!\\.('.$e.')$!i', $archive)) continue;
    $command = $c;
    break;
  }
  if (! $command) return XL("Unsupported archive:")." '$archive'";
  
  $command = sprintf($command, $archive, $fname);
  
  exec($command, $output, $rval);
  $content = implode("\n", $output);
  
  if($rval) return sprintf(XL("Command <code>%s</code> returned error code '%s'."), $command, $rval);
  
  $lines = @$opt['lines'];
  if(!$lines) $lines = intval(@$opt['line']);
  
  if($lines) {
    $clines = explode("\n", $content);
    $count = count($clines);
    
    if(preg_match('!^(\\d+)(?:-+|\\.\\.)(\\d+)$!', $lines, $m)) {
      $start = max(1, min(intval($m[1]), intval($m[2]))  ); 
      $end = min($count, max( intval($m[1]), intval($m[2]) )   );
    }
    elseif(preg_match('!^(\\d+)$!', $lines, $m)) {
      $start = max(1, min($count, intval($lines)-$ZCode['SingleLinePad']));
      $end = min($count, intval($lines)+$ZCode['SingleLinePad']);
    }
    else {
      return XL("Unsupported line format:")." '$lines'";
    }
    
    $linestart = $start-1;
    $linelength  =  abs($end - $linestart);
    
    $clines = array_slice($clines, $linestart, $linelength);
    $content = implode("\n", $clines);
  }
  
  $lang = @$opt['lang']? $opt['lang'] : (@$ZCode['Langs'][$ext] ? $ZCode['Langs'][$ext] : $ext);
  
  SDVA($HTMLHeaderFmt, array('ZCode' => '
    <link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.12.0/styles/default.min.css" />
    <script src="https://cdnjs.cloudflare.com/ajax/libs/highlight.js/9.12.0/highlight.min.js"></script>
  '));
  SDVA($HTMLFooterFmt, array('ZCode' => '
    <script>
      hljs.initHighlightingOnLoad();
      function spad(current, last){ // space pad
        current = ""+current.toString();
        last = ""+last.toString();
        var diff = last.length - current.length;
        for(var i=diff; i>0; i--) current = " "+current;
        return current;
      }
      setTimeout(function(){
        var codes = document.querySelectorAll("pre > code[data-linestart]");
        if(!codes.length) return;
        for(var i=0; i<codes.length; i++) {
          var code = codes[i];
          var linestart = parseInt(code.getAttribute("data-linestart"));
          var html = code.innerHTML.split(/\n/);
          var newhtml = "";
          for(var j=0; j<html.length; j++) {
            var current = spad(linestart+j, linestart+html.length);
            newhtml += "<span class=\'hljs-comment\'>"+current+"</span>  "+html[j]+"\n";
          }
          code.innerHTML = newhtml;
        }
      }, 500);
    </script>
  '));
  return "<:block>" .Keep("<pre><code class='$lang' data-linestart='".($linestart+1)."'>".PHSC($content)."</code></pre>");
}