diff --git a/highbeam b/highbeam index 1ff4e0e..782fe08 100755 --- a/highbeam +++ b/highbeam @@ -27,10 +27,12 @@ Underline: $U Usage: highbeam [-c] [-h] [-f config] -c Be case sensitive + -D Print current rules, not the input -h Help -f Define config file (default ~/.highbeamrc) -r Define rules with a string, replaces the other rules -r '\''"land[^[:space:]]\+" "$G" "sky" "$B"'\'' + ' } @@ -72,7 +74,8 @@ U="${E}4m" CONF_FILE=~/.highbeamrc CONF_LINE="" FLAGS="ig" -while getopts chf:r: opt +PRINT=0 +while getopts chf:r:D opt do case "$opt" in c) FLAGS="g" @@ -87,6 +90,9 @@ do case "$opt" in r) CONF_LINE=( $( eval echo $OPTARG ) ) ;; + D) + PRINT=1 + ;; esac done @@ -118,9 +124,28 @@ done rIndex=$(( $rIndex + 1 )) done } +[[ $PRINT -eq 1 ]] && { + echo "From $CONF_FILE:" + for (( rIndex=0; rIndex<${#RULES[@]}; rIndex++ )); + do printf "%s\\t%s" "${RULES[$rIndex]}" "${RULES[$(( $rIndex + 1 ))]}" + rIndex=$(( $rIndex + 1 )) + done + echo "From argument switches:" + for (( rIndex=0; rIndex<${#CONF_LINE[@]}; rIndex++ )); + do printf "%s\\t%s" "${CONF_LINE[$rIndex]}" "${CONF_LINE[$(( $rIndex + 1 ))]}" + rIndex=$(( $rIndex + 1 )) + done + echo "From HB_RULES environment:" + for (( rIndex=0; rIndex<${#CONF_ENV[@]}; rIndex++ )); + do printf "%s\\t%s" "${CONF_ENV[$rIndex]}" "${CONF_ENV[$(( $rIndex + 1 ))]}" + rIndex=$(( $rIndex + 1 )) + done #~ echo ${CONF_ENV[@]} #~ echo $HB_RULES #~ echo $REGEX + exit +} + [[ -z "$REGEX" ]] && { cat - } || { diff --git a/reporting/markslider.py b/reporting/markslider.py index 5f91e3e..e78d270 100755 --- a/reporting/markslider.py +++ b/reporting/markslider.py @@ -1,4 +1,5 @@ #!/usr/bin/env python +# coding=utf-8 # # Copyright 2015 Ville Rantanen # @@ -19,12 +20,14 @@ '''Markslider: a slideshow engine based on markdown.''' __author__ = "Ville Rantanen " - -__version__ = "0.1" +__version__ = "0.6" import sys,os,argparse,re from argparse import ArgumentParser -import traceback,tty,termios +import traceback,tty,termios,subprocess + +HL=">" +EOS="# End of Slides" class bc: K="\033[1;30m" @@ -47,6 +50,7 @@ class bc: U = '\033[4m' Z = '\033[0m' CLR = '\033[2J' + CLREND = '\033[K' color_keys="KRGBYMCWkrgbymcwSUZ" color_list=[K,R,G,B,Y,M,C,W,k,r,g,b,y,m,c,w,S,U,Z] @@ -59,6 +63,8 @@ class bc: def clear(self): sys.stdout.write( self.CLR+self.pos(0,0) ) + def clear_to_end(self): + sys.stdout.write( self.CLREND ) def color_string(self,s): for i,c in enumerate(self.color_keys): @@ -68,7 +74,6 @@ class bc: for i,c in enumerate(self.color_keys): s=s.replace("${"+c+"}","") return s - class getch: def get(self): @@ -83,7 +88,7 @@ class getch: class EndProgram( Exception ): - ''' Nice way of exiting the program ''' + ''' Nice exit ''' pass class slide_reader: @@ -108,17 +113,27 @@ class slide_reader: self.pages=0 self.data=[] new_page=[] + first_slide_found=False for row in f: if not row: continue row=row.decode('utf-8').rstrip("\n\r ") + # find end of show + if row==EOS: + break + # find header to start a new page if row.startswith("#") and not row.startswith("##"): + first_slide_found=True if len(new_page)>0: self.data.append(new_page) new_page=[] - new_page.append(row) + # if first slide havent been found yet: + if not first_slide_found: + continue + new_page.extend(self.launch(row)) if len(new_page)>0: self.data.append(new_page) + self.toc() self.pages=len(self.data) self.inc_page_no(0) @@ -180,15 +195,36 @@ class slide_reader: if self.opts.toc_depth==3: continue if re.search("^####[^#]", line): subh=[ subh[0], subh[1], subh[2]+1 ] - TOC.append(" %d.%d.%d.%d. %s"%(h1+1,subh[0],subh[1],subh[2],title)) + TOC.append(" %d.%d.%d.%d. %s"%(h1+1,subh[0],subh[1],subh[2],title)) self.data.insert(self.opts.toc_page-1,TOC) + def launch(self,s): + """ Launch in a string using tags $!command$! + Remove empty lines from beginning and end of stdout. + """ + if not self.opts.execute_read: + return [s] + if s.find("$>")==-1: + return [s] + command=re.match("(.*)\$>(.*)\$>(.*)",s) + if command != None: + output = subprocess.check_output(command.group(2).strip(),shell=True).split("\n") + while len(output[0].strip())==0: + if len(output)==1: return [""] + del output[0] + while len(output[-1].strip())==0: + if len(output)==1: return [""] + del output[-1] + return_value=[command.group(1)] + return_value.extend(output) + return_value.append(command.group(3)) + return return_value + return [s] - def get_interactive_help_text(): - return ''' left/right,page up/down,home,end + return ''' left/right,page up/down,home,end change page - m toggle page numbers + s toggle status bar q exit browser r reload the file ,/. scroll page @@ -198,14 +234,20 @@ def get_interactive_help_text(): def setup_options(): ''' Create command line options ''' usage=''' - - Interactive mode keymap: +MarkSlider: a markdown based slideshow engine +Special syntaxes: + * Colors: insert string ${C}, where C is one of KRGBYMCWkrgbymcwSUZ + * Text before first "# header" is not shown + * Text after a "# End of Slides" is not shown + * Execute shell: "$! command -switch $!" Beware of malicious code! + * Execute and print output: "$> command $>" Beware of malicious code! + +Keyboard shortcuts: '''+get_interactive_help_text() parser=ArgumentParser(description=usage, formatter_class=argparse.RawDescriptionHelpFormatter, epilog=__author__) - parser.add_argument("-v","--version",action="version",version=__version__) @@ -219,8 +261,12 @@ def setup_options(): help="Disable status bar.") parser.add_argument("-w",action="store_false",dest="wrap",default=True, help="Disable line wrapping. Cuts long lines.") + parser.add_argument("-e",action="store_true",dest="execute",default=False, + help="Execute commands in $! or $> tags at show time with Enter key. WARNING: Potentially very dangerous to run others' slides with this switch!") + parser.add_argument("-E",action="store_true",dest="execute_read",default=False, + help="Execute commands in $> tags at file read time. WARNING: Potentially very dangerous to run others' slides with this switch!") parser.add_argument("--toc",action="store",dest="toc",default=False, - const="Table of contents", type=str, nargs='?', + const="Table of Contents", type=str, nargs='?', help="Insert table of contents. Define the header, or use default: %(const)s") parser.add_argument("--toc-page",action="store",dest="toc_page",default=2, type=int, help="Insert table of contents on a chosen page. default: %(const)s") @@ -232,7 +278,6 @@ def setup_options(): opts=parser.parse_args() return opts - def page_print(reader,opts,offset): ''' Print a page ''' @@ -256,8 +301,8 @@ def page_print(reader,opts,offset): else: row_lines=int(float(len(row))/scrsize[1]) colored=colorify(row,opts) - if offset[1]==r+1: - colored=colorify("${Y}>"+bc.nocolor_string(row),opts) + if offset[1]==r+1+offset[0]: + colored=add_highlight(row,opts) sys.stdout.write(colored) if r>=scrsize[0]-2: @@ -292,13 +337,8 @@ def offset_change(opts,reader,offset,new_offset): ''' Change the display position of page ''' new_offset=(offset[0]+new_offset[0], offset[1]+new_offset[1]) offsety=min(reader.get_page_height()-1,new_offset[0]) - offsety=max(0,offsety) - if offsety==0 and new_offset[0]!=0: - return (offsety,offset[1]) - offseth=min(opts.size[0]-1,new_offset[1]) - offseth=min(reader.get_page_height(),offseth) - offseth=max(0,offseth) - return (offsety, offseth) + offseth=min(reader.get_page_height(),new_offset[1]) + return [max(0,o) for o in (offsety,offseth)] def browser(opts,filename): ''' Main function for printing ''' @@ -338,32 +378,30 @@ def browser(opts,filename): offset=(0, 0) if inkey==ord('h'): print_help(reader,opts) - if inkey==ord('m'): + if inkey==ord('s'): opts.menu=not opts.menu if inkey==ord('r'): reader.read() - offset=(0, 0) + offset=offset_change(opts,reader,offset,(0, 0)) if inkey==ord(','): - offset=offset_change(opts,reader,offset,(-1, 1)) + offset=offset_change(opts,reader,offset,(-1, 0)) if inkey==ord('.'): - offset=offset_change(opts,reader,offset,(1, -1)) + offset=offset_change(opts,reader,offset,(1, 0)) if inkey==65: offset=offset_change(opts,reader,offset,(0, -1)) if inkey==66: offset=offset_change(opts,reader,offset,(0, 1)) + if inkey==13: + launch(reader,opts,offset) break - #~ stdscr.refresh() - except IOError: pass except KeyboardInterrupt: - #~ reset_curses(stdscr) sys.exit(0) except EndProgram: pass except: - #~ reset_curses(stdscr) print "Unexpected error:" print traceback.format_exc() sys.exit(1) @@ -382,6 +420,7 @@ def colorify(s,opts): "(^#.*)", ## Header "(^\s\s\s\s[^\*0-9\s].*)", # code block "(\`[^\s]*[^\`]+\`)", # code inline + "(\$[>!].*\$[>!])", # code tags "(\[[^]]+\]\([^\)]+\))", # [link](url) "(\*{1,2}[^\s]*[^\*\s]+\*{1,2})", # *bold* "(_[^\s]*[^_\s]+_)", # _bold_ @@ -392,6 +431,7 @@ def colorify(s,opts): "${U}${b}\\1", ## Header "${m}\\1", # code block "${m}\\1${Z}", # code inline + "${m}\\1${Z}", # code tags "${B}${U}\\1${Z}", # [link](url) "${W}\\1${Z}", # *bold* "${W}\\1${Z}", # _bold_ @@ -402,6 +442,7 @@ def colorify(s,opts): "${U}${Y}\\1", ## Header "${c}\\1", # code block "${c}\\1${Z}", # code inline + "${c}\\1${Z}", # code tags "${B}${U}\\1${Z}", # [link](url) "${W}\\1${Z}", # *bold* "${W}\\1${Z}", # _bold_ @@ -419,7 +460,48 @@ def cut_line(s,i): re.sub("\$\{.$","", s))) return s +def add_highlight(s,opts): + if len(s.strip())==0: + cleaned=HL + else: + cleaned=bc.nocolor_string(s) + tagged="${Y}"+cleaned + return colorify(tagged,opts) +def launch(reader,opts,offset): + """ Launch in a string using tags $!command$! + Used with highlight + Remove empty lines from beginning and end of stdout. + """ + if not opts.execute: + return + s=reader.get_current_page()[offset[1]] + if s.find("$!")+s.find("$>")==-2: + return + + run_command=re.match("(.*)\$!(.*)\$!(.*)",s) + show_command=re.match("(.*)\$>(.*)\$>(.*)",s) + if show_command != None: + output = subprocess.check_output(show_command.group(2).strip(),shell=True).split("\n") + while len(output[0].strip())==0: + if len(output)==1: return [""] + del output[0] + while len(output[-1].strip())==0: + if len(output)==1: return [""] + del output[-1] + for y,l in enumerate(output): + bc.posprint(y+offset[1]-offset[0]+2,0,l) + bc.clear_to_end() + inkey=getch.get() + return + if run_command != None: + subprocess.call(run_command.group(2), + #~ stdout=subprocess.PIPE, + #~ stderr=subprocess.PIPE, + shell=True,executable="/bin/bash") + inkey=getch.get() + return + return bc=bc() getch=getch() opts=setup_options()