Guest

kingbash.py

By: a guest on Aug 6th, 2010  |  syntax: Python  |  size: 20.38 KB  |  hits: 249  |  expires: Never
download  |  raw  |  embed  |  report abuse
This paste has a previous version, view the difference. Copied
  1. #! /usr/bin/env python
  2. import os, os.path, re, stat, termios, fcntl, sys, string, operator, struct
  3.  
  4. def main():
  5.         try:
  6.                 point=int(os.environ["READLINE_POINT"])
  7.         except:
  8.                 print """Usage:
  9. function kingbash.fn() {
  10.  echo -n "KingBash> $READLINE_LINE" #Where "KingBash> " looks best if it resembles your PS1, at least in length.
  11.  OUTPUT=`"""+sys.argv[0]+""" "$(compgen -ab -A function)"`
  12.  READLINE_POINT=`echo "$OUTPUT" | tail -n 1`
  13.  READLINE_LINE=`echo "$OUTPUT" | head -n -1`
  14.  echo -ne "\\r\\e[2K"; }
  15. bind -x '"\\t":kingbash.fn'
  16.  
  17. History search usage:
  18. function kingbash.hs() {
  19.  echo -n "KingBash> $READLINE_LINE" #Where "KingBash> " looks best if it resembles your PS1, at least in length.
  20.  history -a
  21.  OUTPUT=`"""+sys.argv[0]+""" -r <(tac ~/.bash_history)`
  22.  READLINE_POINT=`echo "$OUTPUT" | tail -n 1`
  23.  READLINE_LINE=`echo "$OUTPUT" | head -n -1`
  24.  echo -ne "\\r\\e[2K"; }
  25. bind -x '"\\x12":kingbash.hs'
  26.  
  27. CLI dmenu:
  28. ... | """+sys.argv[0]+""" -r <(cat) | head -n 1 | ...
  29.  
  30. GUI dmenu:
  31. rm /tmp/menu
  32. cat > /tmp/inmenu
  33. urxvtc -e bash -c 'export READLINE_POINT=0; answ=$(""" + sys.argv[0] + """ -r /tmp/inmenu | head -n 1); echo -n "$answ" > /tmp/menu'
  34. while sleep 0.1; do [ -f /tmp/menu ] && break; done
  35. cat /tmp/menu
  36. rm /tmp/menu
  37. rm /tmp/inmenu
  38. """
  39.                 sys.exit(0)
  40.         try:
  41.                 line=os.environ["READLINE_LINE"]
  42.         except KeyError:
  43.                 line=""
  44.         if len(sys.argv) > 2 and sys.argv[1] == "-r":
  45.                 file_mode=True
  46.                 f=open(sys.argv[2])
  47.                 read_file=[]
  48.                 for read_f in f:
  49.                         read_file.append(read_f[:-1])
  50.                 f.close()
  51.                 suggestions=grep_list(line.split(), read_file)
  52.                 filedict=[]
  53.                 for suggestion in suggestions:
  54.                         filedict.append( { "isdir":False, "name":suggestion, "format":"\x1b[m"+suggestion, "rec":False })
  55.                 prework=""
  56.                 workpath=""
  57.                 is_command=True
  58.                 upto=""
  59.                 workstr=""
  60.                 postwork=""
  61.                 cursor=point
  62.         else:
  63.                 file_mode=False
  64.                 if line[-2:] == "..": print line+"/"; print point+1; return
  65.                 upto=line[:point]
  66.                 first_space=re.search("[^\\\\] ", upto)
  67.                 if first_space:
  68.                         first_space=first_space.start()
  69.                 else:
  70.                         first_space=point
  71.                 workstr=re.match("(.*(?<!\\\\)[ &;|=])(.*)", upto)
  72.                 if not workstr:
  73.                         prework=""
  74.                         workstr=upto
  75.                 else:
  76.                         prework=workstr.group(1)
  77.                         workstr=workstr.group(2)
  78.                 postwork=line[point:]
  79.                 workpath=re.match("(.*/)(.*)",workstr)
  80.                 if not workpath:
  81.                         workpath=""
  82.                 else:
  83.                         workstr=workpath.group(2)
  84.                         workpath=workpath.group(1)
  85.                 if len(workstr) != 0  and workstr[0] == "'":
  86.                         workstr=unfixshell_singlequote(workstr)
  87.                 else:
  88.                         workstr=unfixshell(workstr)
  89.                 filedict=[]
  90.        
  91.                 is_command=False
  92.                 if len(workpath) == 0:
  93.                         if len(prework) == 0: is_command=True
  94.                         else:
  95.                                 if point <= first_space: is_command=True
  96.                                 if prework[-5:] in ("sudo ", "type "): is_command=True
  97.                                 if prework[-6:] in ("which "): is_command=True
  98.                                 if prework[-7:] in ("whatis "): is_command=True
  99.                                 if prework[-8:] in ("whereis "): is_command=True
  100.                                 if prework[-1]  in ("|", ";", "&") : is_command=True
  101.                                 if prework[-2:] in ("| ","; ","& "): is_command=True
  102.                                 #However,
  103.                                 if prework[-1]  == "=" : is_command=False
  104.                                 if prework[-2:] == "= ": is_comamnd=False
  105.        
  106.                 if is_command:
  107.                         suggestions=complete_command(workstr)
  108.                 else:
  109.                         suggestions=complete_filename(workstr, unfixshell(workpath))
  110.                 suggestions=sorted(set(suggestions),key=str.lower)
  111.                 if len(suggestions) == 1 and not (len(workstr) == 0 and len(sys.argv) > 2 and sys.argv[2] == "rerun" ):
  112.                         fix=fixshell(suggestions[0])
  113.                         if not os.path.isdir(os.path.expandvars(os.path.expanduser(unfixshell(workpath))) + suggestions[0]):
  114.                                 fix+=" "
  115.                         else:
  116.                                 fix+="/"
  117.                                 if len(sys.argv) > 2 and sys.argv[2] == "rerun":
  118.                                         rerun_fn(prework+workpath+fix+postwork, str(len(prework+workpath+fix)))
  119.                         print prework + workpath + fix + postwork
  120.                         print len(prework + workpath + fix)
  121.                         return
  122.                 if len(suggestions) == 0:
  123.                         if len(workstr) != 0 or not ( len(sys.argv) > 2 and sys.argv[2] == "rerun" ):
  124.                                 print line
  125.                                 print point
  126.                                 return
  127.                         else:
  128.                                 suggestions=[""]
  129.                 if len(workstr) > 0 and workstr[-1] == '\\': workstr=workstr[:-1] #Don't escape the escape for a future character
  130.                 first=suggestions[0]
  131.                 lcd=[] #find largest common divider
  132.                 for last in suggestions[1:]:
  133.                         ld=""
  134.                         for i in xrange(min(len(first),len(last))):
  135.                                 if first[i].upper()==last[i].upper():
  136.                                         ld=ld+first[i]
  137.                                 else:
  138.                                         break
  139.                         lcd.append(ld)
  140.                 if len(lcd) > 0:
  141.                         smallest=sorted(lcd)[0]
  142.                 else:
  143.                         smallest=""
  144.                 if len(smallest) > len(workstr):
  145.                         workstr=smallest
  146.                 line=prework+workpath+fixshell(workstr)+postwork
  147.                 point=len(prework+workpath+fixshell(workstr))
  148.                 cursor=len(workpath+workstr)
  149.                 if is_command:
  150.                         for suggestion in suggestions:
  151.                                 filedict.append({ "isdir":False, "name":suggestion, "format":"\x1b[7m" + suggestion[:cursor] + "\x1b[m" + suggestion[cursor:], "rec":False })
  152.                 else:
  153.                         for suggestion in suggestions:
  154.                                 statf=os.lstat(os.path.expandvars(os.path.expanduser(unfixshell(workpath)))+suggestion)
  155.                                 link_ind=""
  156.                                 if stat.S_ISLNK(statf[stat.ST_MODE]):
  157.                                         link_ind=" -> "+os.readlink(os.path.expandvars(os.path.expanduser(unfixshell(workpath)))+suggestion)
  158.                                         try:
  159.                                                 statf=os.stat(os.path.expandvars(os.path.expanduser(unfixshell(workpath)))+suggestion)
  160.                                                 link_ind=" @"+link_ind
  161.                                         except OSError:
  162.                                                 link_ind=" @BAD"+link_ind
  163.                                 size=statf[stat.ST_SIZE]
  164.                                 exp=0
  165.                                 while size >= 1024: size=size/1024; exp=exp+1
  166.                                 size=str(size)+["B","KB","MB","GB","TB"][exp]
  167.                                 if stat.S_ISDIR(statf[stat.ST_MODE]):
  168.                                         dirappend="/"
  169.                                 else:
  170.                                         dirappend=""
  171.                                 if point > first_space and len(prework.split()) > 0 and isrecommended(re.sub(".*[^\\\\][;&|]", "", prework).split()[0], suggestion, dirappend=="/"):
  172.                                         rec=True
  173.                                         color="\x1b[m\x1b[31m"
  174.                                         matchcolor="\x1b[31;47m"
  175.                                 else:
  176.                                         rec=False
  177.                                         color="\x1b[m"
  178.                                         matchcolor="\x1b[30;47m"
  179.                                 filedict.append({ "isdir":dirappend=="/", "name":suggestion, "format":"\x1b[m"+string.ljust("\x1b[35m(" + size + ")\x1b[m", 17) + matchcolor + str(workpath+suggestion)[:cursor] + color + str(workpath+suggestion)[cursor:] + dirappend + link_ind, "rec":rec})
  180.                 filedict.sort(key=operator.itemgetter("isdir"),reverse=True)
  181.                 filedict.sort(key=operator.itemgetter("rec"),reverse=True)
  182.  
  183.         selected=0
  184.         viewmin=0
  185.         if len(sys.argv) == 5:
  186.                 selected=int(sys.argv[3])
  187.                 viewmin=int(sys.argv[4]) #Restoring the position from Insert
  188.         try:
  189.                 screen=terminal_size()[0]/3
  190.                 if screen == 0: screen=0/0
  191.         except:
  192.                 screen=23
  193.         original_screen=screen
  194.  
  195.         count=len(filedict)-1
  196.         if count < screen: screen=count
  197.  
  198.         #This disables line wrap, makes the cursor invisible, and clears the possible calling-function's replacement prompt
  199.         sys.stderr.write("\x1b[?7l\x1b[?25l\r\x1b[2K")
  200.  
  201.         fd=sys.stdin.fileno()
  202.         oldterm=termios.tcgetattr(fd)
  203.         newattr=termios.tcgetattr(fd)
  204.         newattr[3]=newattr[3] & ~termios.ICANON & ~termios.ECHO
  205.         termios.tcsetattr(fd, termios.TCSANOW, newattr)
  206.        
  207.         file_mode_changes=False
  208.         rerun=False
  209.         icm=False #icm means Insert's Cursor Move.
  210.  
  211.         try:
  212.                 while 1:
  213.                         if file_mode and file_mode_changes:
  214.                                 suggestions=grep_list(line.split(), read_file)
  215.                                 filedict=[]
  216.                                 for suggestion in suggestions:
  217.                                         filedict.append( { "isdir":False, "name":suggestion, "format":"\x1b[m"+suggestion, "rec":False })
  218.                                 oldcount=count
  219.                                 count=len(filedict)-1
  220.                                 if count < 0: count = 0
  221.                                 if count < original_screen:
  222.                                         screen=count
  223.                                 if screen != original_screen and count > original_screen:
  224.                                         screen=original_screen
  225.                                 if oldcount != count: selected=0
  226.                                 file_mode_changes=False
  227.                         try:
  228.                                 unused = line[point+1:]
  229.                                 sys.stderr.write("KingBash> " + line[:point] + '\x1b[7m' + line[point] + '\x1b[m' + line[point+1:] + '\n')
  230.                         except IndexError:
  231.                                 sys.stderr.write("KingBash> " + line + '\x1b[7m \x1b[m\n')
  232.                         show_filelist(filedict, selected, viewmin, screen)
  233.                         try:
  234.                                 c=sys.stdin.read(1)
  235.                                 if c == "\x1b":
  236.                                         c="ESC"
  237.                                         oldflags=fcntl.fcntl(fd, fcntl.F_GETFL)
  238.                                         fcntl.fcntl(fd, fcntl.F_SETFL, oldflags | os.O_NONBLOCK)
  239.                                         while 1:
  240.                                                 try:
  241.                                                         newc=sys.stdin.read(1)
  242.                                                         c=c+newc
  243.                                                         if newc in ("A","B","C","D","a","b","c","d"):
  244.                                                                 break
  245.                                                 except IOError: break
  246.                                         fcntl.fcntl(fd, fcntl.F_SETFL, oldflags)
  247.                                 if   c == "ESC[A":#UP
  248.                                         selected-=1
  249.                                 elif c in ("ESC[a","ESC[1;2A"):#SHIFT UP
  250.                                         selected-=5
  251.                                 elif c == "ESC[B":#DOWN
  252.                                         selected+=1
  253.                                 elif c in ("ESC[b","ESC[1;2B"):#SHIFT DOWN
  254.                                         selected+=5
  255.                                 elif c == "ESC[C":#RIGHT
  256.                                         if file_mode:
  257.                                                 if point != len(line):
  258.                                                         point+=1
  259.                                         else:
  260.                                                 filedict[selected]["name"]=fixshell(filedict[selected]["name"])
  261.                                                 if not filedict[selected]["isdir"]:
  262.                                                         filedict[selected]["name"]+=" "
  263.                                                 else:
  264.                                                         filedict[selected]["name"]+="/"
  265.                                                 line=prework + workpath + filedict[selected]["name"] + postwork
  266.                                                 point=len(prework + workpath + filedict[selected]["name"])
  267.                                                 rerun=True
  268.                                                 break
  269.                                 elif c == "ESC[D":#LEFT
  270.                                         if file_mode:
  271.                                                 if point != 0:
  272.                                                         point-=1
  273.                                         else:
  274.                                                 workstr=""
  275.                                                 oldlen=len(workpath)
  276.                                                 if workpath == "/":
  277.                                                         pass
  278.                                                 elif workpath == "" or workpath[-3:] == "../":
  279.                                                         workpath+="../"
  280.                                                 else:
  281.                                                         oldpath=workpath
  282.                                                         workpath=re.sub("/[^/]*/$", "/", workpath)
  283.                                                         if oldpath == workpath:
  284.                                                                 workpath=re.sub(".*?/$", "", workpath)
  285.                                                         del oldpath
  286.                                                 line=prework+workpath+fixshell(workstr)+postwork
  287.                                                 point=point+(len(workpath)-oldlen)
  288.                                                 rerun=True
  289.                                                 break
  290.                                 elif c == "ESC[6~":#PAGEDOWN
  291.                                         selected+=screen+1
  292.                                         if viewmin + screen + 1 < count: viewmin+=screen+1
  293.                                 elif c == "ESC[5~":#PAGEUP
  294.                                         selected-=screen+1
  295.                                         viewmin-=screen+1
  296.                                 elif c in ("ESC[4~","ESC[F","ESC[8~"):#END
  297.                                         selected=count
  298.                                         viewmin=count - (screen - (count % screen))
  299.                                 elif c in ("ESC[1~","ESC[H","ESC[7~"):#HOME
  300.                                         selected=0
  301.                                         viewmin=0
  302.                                 elif c in ("\x0d", "\n"):#ENTER
  303.                                         if not file_mode:
  304.                                                 filedict[selected]["name"]=fixshell(filedict[selected]["name"])
  305.                                                 if not filedict[selected]["isdir"]:
  306.                                                         filedict[selected]["name"]+=" "
  307.                                                 else:
  308.                                                         filedict[selected]["name"]+="/"
  309.                                         try:
  310.                                                 line=prework + workpath + filedict[selected]["name"] + postwork
  311.                                                 point=len(prework + workpath + filedict[selected]["name"])
  312.                                         except IndexError:
  313.                                                 pass
  314.                                         break
  315.                                 elif c == "ESC":#ESCAPE
  316.                                         if file_mode:
  317.                                                 line=""
  318.                                                 point=0
  319.                                         break
  320.                                 elif c in ("\x7f", "\x08"):#BACKSPACE
  321.                                         if file_mode:
  322.                                                 if point != 0:
  323.                                                         line=line[:point-1]+line[point:]
  324.                                                         point-=1
  325.                                                         file_mode_changes=True
  326.                                         else:
  327.                                                 line=prework+str(workpath+workstr)[:-1]+postwork
  328.                                                 point-=1
  329.                                                 rerun=True
  330.                                                 break
  331.                                 elif c in ("ESC\x7f", "ESC\x08"):#ALT+BACKSPACE
  332.                                         if file_mode:
  333.                                                 try:
  334.                                                         nline=" ".join(line[:point].split(" ")[:-1])+line[point:]
  335.                                                         point-=len(line)-len(nline)
  336.                                                         line=nline
  337.                                                         del nline
  338.                                                         file_mode_changes=True
  339.                                                 except: pass
  340.                                         else:
  341.                                                 new=re.sub("""[/ '"]?[^/ '"]*?[/ '"]?$""", "", prework+workpath+fixshell(workstr))
  342.                                                 line=new+postwork
  343.                                                 point=len(new)
  344.                                                 rerun=True
  345.                                                 break
  346.                                 elif c == "ESC[3~":#DELETE
  347.                                         if file_mode:
  348.                                                 line=line[:point]+line[point+1:]
  349.                                                 file_mode_changes=True
  350.                                 elif c == "\x15":#CONTROL + U
  351.                                         line=""
  352.                                         point=0
  353.                                         if not file_mode:
  354.                                                 break
  355.                                 elif c == "\x09":#TAB
  356.                                         pass
  357.                                 elif c == "ESC[2~":#INSERT
  358.                                         if not file_mode:
  359.                                                 filedict[selected]["name"]=fixshell(filedict[selected]["name"])
  360.                                                 if not filedict[selected]["isdir"]:
  361.                                                         filedict[selected]["name"]+=" "
  362.                                                 else:
  363.                                                         filedict[selected]["name"]+="/ "
  364.                                         line=prework + workpath + filedict[selected]["name"] + workpath + fixshell(workstr) + postwork
  365.                                         point=len(prework + workpath + filedict[selected]["name"] + workpath + fixshell(workstr))
  366.                                         if not file_mode:
  367.                                                 rerun=True
  368.                                                 selected+=1
  369.                                                 if selected > count: selected=count
  370.                                                 if selected > viewmin + screen: viewmin+=screen+1
  371.                                                 icm=(str(selected), str(viewmin))
  372.                                                 break
  373.                                 elif c == "*":#ASTERISK
  374.                                         if file_mode: break
  375.                                         line=prework
  376.                                         point=len(prework)
  377.                                         allfalse=True
  378.                                         for file in filedict:
  379.                                                 if file["rec"] == False: continue
  380.                                                 allfalse=False
  381.                                                 file["name"]=fixshell(file["name"])+" "
  382.                                                 line+=workpath+file["name"]
  383.                                                 point+=len(workpath+file["name"])
  384.                                         if allfalse:
  385.                                                 for file in filedict:
  386.                                                         file["name"]=fixshell(file["name"])+" "
  387.                                                         line+=workpath + file["name"]
  388.                                                         point+=len(workpath + file["name"])
  389. #                                       line+=workpath+fixshell(workstr)+postwork
  390. #                                       point+=len(workpath+fixshell(workstr))
  391.                                         line+=postwork
  392. #                                       rerun=True #In case you want to backspace and add more stuff
  393.                                         break
  394.                                 elif c in ("ESC[12~","ESCOQ"):#F2
  395.                                         if not is_command:
  396.                                                 prework=re.sub("^.*?(?<!\\\\) ", "", prework) #remove first word and move cursor there
  397.                                                 prework=" "+prework
  398.                                                 filedict[selected]["name"]=fixshell(filedict[selected]["name"])
  399.                                                 if not filedict[selected]["isdir"]:
  400.                                                         filedict[selected]["name"]+=" "
  401.                                                 else:
  402.                                                         filedict[selected]["name"]+="/"
  403.                                                 line=prework + workpath + filedict[selected]["name"] + postwork
  404.                                                 point=0
  405.                                                 break
  406.                                 else:
  407.                                         if file_mode:
  408.                                                 line=line[:point] + c + line[point:]
  409.                                                 point+=len(c)
  410.                                                 file_mode_changes=True
  411.                                         else:
  412.                                                 point=len(prework + workpath + fixshell(workstr) + c)
  413.                                                 line=prework + workpath + fixshell(workstr) + c + postwork
  414.                                                 if c not in (' ',"="):
  415.                                                         rerun=True
  416.                                                 break
  417.                         except IOError: pass
  418.                         if selected < 0: selected=0
  419.                         elif selected > count: selected=count
  420.                         elif selected > viewmin + screen: viewmin+=screen+1
  421.                         elif selected < viewmin: viewmin-=screen
  422.                         if viewmin < 0: viewmin=0
  423.                         erase_filelist(screen+1)
  424.         except KeyboardInterrupt:
  425.                 if file_mode:
  426.                         line=""
  427.                         point=0
  428.         finally:
  429.                 sys.stderr.write('\x1b[m\x1b[?25h\x1b[?7h') #default color, make cursor visible, enable line wrap
  430.                 erase_filelist(screen+1)
  431.                 termios.tcsetattr(fd, termios.TCSAFLUSH, oldterm)
  432.         if rerun:
  433.                 rerun_fn(line, point, icm)
  434.         else:
  435.                 print line
  436.                 print point
  437.  
  438. def rerun_fn(line,point,icm=None):
  439.         os.environ["READLINE_LINE"]=line
  440.         os.environ["READLINE_POINT"]=str(point)
  441.         if len(sys.argv) > 1:
  442.                 aliases=sys.argv[1]
  443.         else:
  444.                 aliases="alias"
  445.         if icm:
  446.                 os.execl(sys.argv[0],sys.argv[0],aliases,"rerun",icm[0],icm[1])
  447.         else:
  448.                 os.execl(sys.argv[0],sys.argv[0],aliases,"rerun")
  449.  
  450. # Thanks to http://bytes.com/topic/python/answers/453313-best-way-finding-terminal-width-height
  451. def ioctl_GWINSZ(fd): #### TABULATION FUNCTIONS
  452.         try: ### Discover terminal width
  453.                 cr = struct.unpack('hh', fcntl.ioctl(fd, termios.TIOCGWINSZ, '1234'))
  454.         except:
  455.                 return None
  456.         return cr
  457. def terminal_size():
  458. ### decide on *some* terminal size
  459.         # try open fds
  460.         cr = ioctl_GWINSZ(0) or ioctl_GWINSZ(1) or ioctl_GWINSZ(2)
  461.         if not cr:
  462.                 # ...then ctty
  463.                 try:
  464.                         fd = os.open(os.ctermid(), os.O_RDONLY)
  465.                         cr = ioctl_GWINSZ(fd)
  466.                         os.close(fd)
  467.                 except:
  468.                         pass
  469.         return int(cr[0]), int(cr[1])
  470.  
  471. def complete_command(workstr):
  472.         paths=os.environ["PATH"].split(":")
  473.         paths.append("COMPLETE_ALIASES")
  474.         suggestions=[]
  475.         for path in paths:
  476.                 suggestions.extend(complete_filename(workstr, path))
  477.         return suggestions
  478. def complete_filename(workstr, path):
  479.         if path == "": path="./"
  480.         path=os.path.expandvars(os.path.expanduser(path))
  481.         suggestions=[]
  482.         try:
  483.                 if path == "COMPLETE_ALIASES":
  484.                         if len(sys.argv) > 1:
  485.                                 filelist=sys.argv[1].split("\n") #expect a newline separated list on argument 1
  486.                         else:
  487.                                 return []
  488.                 else:
  489.                         filelist=os.listdir(path)
  490.                 for file in filelist:
  491.                         try:
  492.                                 asterisk=re.escape(workstr)
  493.                                 asterisk=re.sub("\\\\\*",".*",asterisk)
  494.                                 asterisk=re.sub("\\\\\?",".?",asterisk)
  495.                                 asterisk+=".*"
  496.                                 asterisk_match=re.match(asterisk,file,re.I)
  497.                                 if asterisk_match:
  498.                                         suggestions.append(file)
  499.                                 else:
  500.                                         asterisk_match=re.match(asterisk,fixshell(file),re.I)
  501.                                         if asterisk_match:
  502.                                                 suggestions.append(file)
  503.                         except ValueError: pass
  504.         except OSError: pass
  505.         return suggestions
  506. def grep_list(patterns, lines):
  507.         good_list=[]
  508.         for line in lines:
  509.                 found=True
  510.                 for pattern in patterns:
  511.                         if pattern not in line:
  512.                                 found=False
  513.                                 break
  514.                 if found: good_list.append(line)
  515.         return good_list
  516.  
  517. def show_filelist(filedict, selected, viewmin, screen):
  518.         length=len(filedict)-1
  519.         eofs=viewmin+screen
  520.         if eofs > length:
  521.                 modeofs=length
  522.         else:
  523.                 modeofs=eofs
  524.         try:
  525.                 if length % screen != 0:
  526.                         modlen=(screen-(length%screen)+length)
  527.                 else:
  528.                         modlen=length
  529.         except ZeroDivisionError:
  530.                 modlen=1
  531.         try:
  532.                 dotstart=screen*viewmin/modlen+viewmin
  533.                 if dotstart == 2: dotstart=1
  534.                 dotend=screen*(eofs+1)/modlen+viewmin-1
  535.                 if modeofs - viewmin == 0: modeofs=2
  536.                 selindicator=dotstart + (dotend - dotstart + 1) * (selected - viewmin) / (modeofs - viewmin +1)
  537.         except ZeroDivisionError:
  538.                 dotstart=1
  539.                 dotend=1
  540.                 selindicator=1
  541.         if selindicator > eofs: selindicator=eofs
  542.         if dotend < dotstart: dotend=dotstart
  543.         for i in xrange(viewmin, viewmin+screen+1):
  544.                 sys.stderr.write("\x1b[m")
  545.                 if i<dotstart or i>dotend:
  546.                         sys.stderr.write("  ")
  547.                 elif i == selindicator:
  548.                         sys.stderr.write("X ")
  549.                 elif i >= dotstart and i <= dotend:
  550.                         sys.stderr.write("* ")
  551.                 try:
  552.                         if i == selected:
  553.                                 format_string=filedict[i]["format"].replace("\x1b[m","\x1b[m\x1b[30;46m")+ "\x1b[36;46m" + " # " * 99
  554.                         else:
  555.                                 format_string=filedict[i]["format"]
  556.                         sys.stderr.write(format_string.replace("\n","(nl)")+"\x1b[m")
  557.                 except IndexError: pass
  558.                 if i != viewmin+screen: sys.stderr.write('\n') 
  559.                
  560. def erase_filelist(screen):
  561.         for unused in xrange(screen):
  562.                 sys.stderr.write("\x1b[2K\x1b[A")
  563.         sys.stderr.write("\x1b[2K\r")
  564.  
  565. def unfixshell_singlequote(goodstr):
  566.         badstr=re.sub("'\\\\''", "'", goodstr)
  567.         if badstr[-1] == "'":
  568.                 return badstr[1:-1]
  569.         else:
  570.                 return badstr[1:]
  571. def unfixshell(goodstr):
  572.         if len(goodstr) != 0 and goodstr[-1] == "\\":
  573.                 appendslash="\\"
  574.         else:
  575.                 appendslash=""
  576.         goodpieces=[]
  577.         while "\\\\" in goodstr:
  578.                 index=goodstr.index("\\\\")
  579.                 goodpieces.append(goodstr[:index])
  580.                 try:
  581.                         goodstr=goodstr[index+2:]
  582.                 except IndexError: goodstr=""
  583.         goodpieces.append(goodstr)
  584.         goodstr=""
  585.         for piece in goodpieces:
  586.                 goodstr=goodstr+re.sub("\\\\","",piece)+"\\"
  587.         return goodstr[:-1]+appendslash
  588. def fixshell(badstr):
  589.         badchars=""" !@#$^&*()`'"<>[]{};:\\=?|  """
  590.         goodstr=""
  591.         for ch in badstr:
  592.                 if ch in badchars:
  593.                         ch="\\"+ch
  594.                 goodstr=goodstr+ch
  595.         return goodstr
  596.  
  597. def isrecommended(cmd, file, isdir):
  598.         if cmd in ("cd","popd","rmdir"):
  599.                 return isdir
  600. # http://wiki.archlinux.org/index.php/Common_Applications
  601. # http://wiki.archlinux.org/index.php/Lightweight_Applications
  602. # not sure if some of the binaries are correctly named
  603.  
  604. #Video (and audio)
  605.         if cmd in ("mplayer", "mwrap", "vlc", "gmplayer", "smplayer", "mencoder", "kmplayer", "Parole", "whaawmp", "dragonplayer","ffmpeg"):
  606.                 return re.search("\.(mkv|m4v|mpe?g|avi|mp4|wmv|rmvb|as[fx]|divx|vob|ogm|rm|flv|part|iso|mp?|ogg|wav|flac|m4a)$",file,re.I) != None
  607.  
  608. #Audio
  609.         if cmd in ("mpg123", "mpg321", "mp3blaster", "cmus", "cplay", "moc", "xmms", "xmms2", "sonata", "deadbeef","ogg123"):
  610.                 return re.search("\.(mp?|wav|ogg|gsm|dct|flac|au|aiff|vox|wma|aac|ra|m4a)$",file,re.I) != None
  611.  
  612. #PDF
  613.         if cmd in ("xpdf","epdfview","evince","foxit","mupdf","okular","apvlv","zathura"):
  614.                 return re.search("\.pdf$",file,re.I) != None
  615.  
  616. #Images
  617.         if cmd in ("feh","geeqie","gqview","eog","gpicview","gthumb","mirage","qiv","ristretto","xnview","xv","viewnior"):
  618.                 return re.search("\.(jpe?g|png|gif|tiff|bmp|ico?n|tif)$",file,re.I) != None
  619.  
  620. #Games
  621.         if cmd in ("sdlmame","openmsx","zsnes","desmume","VirtualBoy"):
  622.                 return re.search("\.(rom|dsk)$",file,re.I) != None
  623.  
  624. #Wine
  625.         if cmd in ("wine", "winewrap", "wineconsole"):
  626.                 return re.search("\.(exe|com|bat)$", file, re.I) != None
  627.  
  628. #Archives
  629.         if cmd in ("atool","x","xi","gunzip","extract","unzip","unrar","zip","rar","7z"):
  630.                 return re.search("\.(tgz|zip|rar|bz2|gz|tar|exe|pk3|lha|Z|lzma)$",file,re.I) != None
  631.  
  632. #Text
  633.         if cmd in ("vim","nano","acme","beaver","geany","leafpad","medit","mousepad","pyroom","sam","vi","gvim","emacs","tea","scite"):
  634.                 return re.search("\.(pdf|rom|dsk|jpe?g|png|gif|tiff|bmp|ico?n|tif|pdf|wav|ogg|gsm|dct|flac|au|aiff|vox|wma|aac|ra|mkv|mpe?g|avi|mp4|wmv|rmvb|as[fx]|divx|vob|ogm|rm|flv|part|iso|mp.|tgz|zip|rar|bz2|gz|tar|exe|pk3|lha|Z|lzma|\.o)$",file,re.I) == None and not isdir #everything else
  635.  
  636.         return False
  637.  
  638. main()