09 March 2011

Snippets in Vim

For common programming snippets in Vim install snipMate plugin.

SnipMate supports autoit, cpp, c, html, javascript, java, mako, objc, perl, php, python, ruby, sh, _, snippet, tcl, tex, vim and zsh file types by default. Default completion is <tab> key. I found it useful to change <tab> to ex. Ctrl+J:
in ~/.vim/after/plugin/snipMate.vim changed
ino <silent> <tab> <c-r>=TriggerSnippet()<cr>
snor <silent> <tab> <esc>i<right><c-r>=TriggerSnippet()<cr>
to
ino <c-j> <c-r>=TriggerSnippet()<cr>
snor <c-j> <esc>i<right><c-r>=TriggerSnippet()<cr>
To use multiple filetypes(as well, as corresponding snippets), you can use whether autoCmd in ~/.vimrc like:
au BufRead *.php set ft=php.html
au BufNewFile *.php set ft=php.html
or modeline like:
/* vim: set ft=html.php: */

Really nice plug-in with intellectual features. E.g. in an HTML file you type:
doctype<c-j>
,
and it suggests to choose one of 7 DOCTYPEs:
HTML 4.01 Strict
HTML 4.01 Transitional
HTML 5
XHTML 1.0 Frameset 
etc.


Enjoy!

UPDATE
Some extra snippets could be found here.

To create your own snippet clone a .snippet file in ~/.vim/snippets/ and modify it on your needs. For instance, I've created this one:

$ cat ~/.vim/snippets/sql/s3_localized.snippet
INSERT INTO \`s3_localized\` (\`localized_code\`) VALUES ('${1:SOMETHING}'); 
SET @ins_id:=(SELECT LAST_INSERT_ID());
INSERT INTO \`s3_localized_text\` (\`localized_id\`, \`lang_id\`, \`localized_text\`)
VALUES 
(@ins_id, 1, '${2:rus}'),
(@ins_id, 2, '${3:eng}'),
(@ins_id, 3, '$3'),
(@ins_id, 4, '$3'),
(@ins_id, 5, '$3'),
(@ins_id, 6, '$3'),
(@ins_id, 7, '$3');

It generates
INSERT INTO `s3_localized` (`localized_code`) VALUES ('SOMETHING'); 
SET @ins_id:=(SELECT LAST_INSERT_ID());
INSERT INTO `s3_localized_text` (`localized_id`, `lang_id`, `localized_text`)
VALUES 
(@ins_id, 1, 'rus'),
(@ins_id, 2, 'eng'),
(@ins_id, 3, 'eng'),
(@ins_id, 4, 'eng'),
(@ins_id, 5, 'eng'),
(@ins_id, 6, 'eng'),
(@ins_id, 7, 'eng');

Ctrl+J moves to the next variable declared in snippet. $-references allow change repeated strings simultaneously(as 'eng' above)

08 March 2011

Cyrillic-to-English key mapping in Vim and Vimperator

To avoid switching between keyboard layouts in navigation, visual mode etc. within a mixed Cyrillic/English text append the following to your ~/.vimrc:

set langmap=ФИСВУАПРШОЛДЬТЩЗЙКЫЕГМЦЧНЯ;ABCDEFGHIJKLMNOPQRSTUVWXYZ,фисвуапршолдьтщзйкыегмцчня;abcdefghijklmnopqrstuvwxyz
Similar mapping works for Vimperator. If you use Vimperator, append the following whether to ~/.vimperatorrc, or ~/.vimperatorrc.local(where appropriate):

map пе gt
map пЕ gT

map Ф A
map И B
map С C
map В D
map У E
map А F
map П G
map Р H
map Ш I
map О J
map Л K
map Д L
map Ь M
map Т N
map Щ O
map З P
map Й Q
map К R
map Ы S
map Е T
map Г U
map М V
map Ц W
map Ч X
map Н Y
map Я Z
map ф a
map и b
map с c
map в d
map у e
map а f
map п g
map р h
map ш i
map о j
map л k
map д l
map ь m
map т n
map щ o
map з p
map й q
map к r
map ы s
map е t
map г u
map м v
map ц w
map ч x
map н y
map я z

08 February 2011

Export all Tomboy notes into HTML/XHTML

Just wrote Python script to export all the Tomboy notes into an HTML file. The following script connects Tomboy via DBus Interface, creates XHTML file and converts it to HTML by means of an XSL. Conversion powered by xsltproc utility.

tomboy2html.py

#!/usr/bin/python
# Copyright (C) 2011 - Ruslan Osmanov <rrosmanov@gmail.com>
# 
# This program is free software; 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 2
# of the License, or (at your option) any later version.
# 
# This program is distributed in the hope that it will be useful,
# but WITHOUT ANY WARRANTY; without even the implied warranty of
# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
# GNU General Public License for more details.
# 
# You should have received a copy of the GNU General Public License
# along with this program; if not, write to the Free Software
# Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
# See <http://www.gnu.org/licenses/>
# 
##
# @file tomboy2html.py
# @brief Export all Tomboy notes in XHTML/HTML file(s)
# @author Ruslan Osmanov
# @version 1.0
# @date 08.02.2011
# @details xsltproc utility required
# @copyright Copyright (C) 2011 - Ruslan Osmanov

import dbus, dbus.glib
try:
  import gobject
except ImportError:
  # gobject functions are moved to dbus.glib in this Python version
  pass
import os, sys, getopt
import re

def usage():
  print __file__ + " [OPTIONS]"
  print """Options:
-h, --help    Display help
-p, --prefix    Optional. Output filename prefix. Default: notes
-x, --xhtml   Optional. Generate XHTML file also. Default: off 
-s, --xsl   Optional. XSL for XML-HTML conversion. Default: tomboy-notes.xsl 
-d, --debug   Optional. Debug mode. Default: off 
"""

def main(argv):
  debug_mode = False
  prefix = 'notes'
  xhtml = False
  xsl_filename = os.path.dirname(__file__) + '/tomboy-notes.xsl'

  # Get CLI options
  try:
    opts, args = getopt.getopt(argv[1:], "s:p:xhd", 
        ("xsl=", "prefix=", "debug", "xhtml", "help"))

  except getopt.GetoptError:
    usage()
    sys.exit(2)

  # Save CLI options
  for o, a in opts:
    if o in ('-h', '--help'):
      usage();
      sys.exit();

    elif o in ('-x', '--xhtml'):
      xhtml = True

    elif o in ('-d', '--debug'):
      debug_mode = True

    elif o in ("-p", "--prefix"):
      prefix = os.path.expanduser(a)

    elif o in ("-s", "--xsl"):
      xsl_filename = os.path.expanduser(a)

  xhtml_filename = prefix + '.xhtml'
  html_filename = prefix + '.html'
  if False == os.path.exists(xsl_filename):
    print "XSL file '"+xsl_filename+"' doesn't exist"
    sys.exit(2)

  if debug_mode:
    print "xhtml =", xhtml_filename, "\nhtml =", html_filename
    print "\nxsl =", xsl_filename

  # Access the Tomboy remote control interface
  bus = dbus.SessionBus()
  obj = bus.get_object("org.gnome.Tomboy", "/org/gnome/Tomboy/RemoteControl")
  tomboy = dbus.Interface(obj, "org.gnome.Tomboy.RemoteControl")

  # Get note URIs
  all_notes = tomboy.ListAllNotes()

  # Write each note XML to the XHTML file
  f = open(xhtml_filename, "w")
  f.write('<?xml version="1.0" encoding="utf-8"?>' + 
      '<?xml-stylesheet type="text/xsl" href="' +xsl_filename+'"?>'
      '<notes>')
  for n in all_notes:
    if (debug_mode):
      print "NOTE '"+n+"'"
    xml = re.sub(r'<\?xml[^<]*\?>', '', tomboy.GetNoteCompleteXml(n))
    xml = re.sub(r'\&\#x?.*\;', '', xml)
    f.write(unicode(xml).encode("utf-8"))

  f.write('</notes>')
  f.close()

  # Generate HTML
  cmd = "xsltproc -o '%(html)s' '%(xsl)s' '%(xhtml)s'" % \
      {'html': html_filename.replace("'", "\\'"), 
          'xsl': xsl_filename, 
          'xhtml': xhtml_filename}
      if debug_mode:
        print cmd
  os.system(cmd)

  if xhtml == False:
    os.unlink(xhtml_filename)

if __name__ == "__main__":
    main(sys.argv)

tomboy-notes.xsl

<?xml version='1.0' encoding="utf-8"?>
<xsl:stylesheet xmlns:xsl="http://www.w3.org/1999/XSL/Transform"
xmlns:tomboy="http://beatniksoftware.com/tomboy"
xmlns:notes="http://beatniksoftware.com/tomboy/notes"
xmlns:size="http://beatniksoftware.com/tomboy/size"
xmlns:link="http://beatniksoftware.com/tomboy/link"
version='1.0'>

<xsl:output method="html" indent="no" />
<xsl:preserve-space elements="*" />

<xsl:param name="font" />
<xsl:param name="export-linked" />
<xsl:param name="export-linked-all" />
<xsl:param name="root-note" />

<xsl:param name="newline" select="'&#xA;'" />

<xsl:template match="/">
<html>
<head>
    <title>Tomboy Notes Export</title>
    <style type="text/css">/*<![CDATA[*/
        body { <xsl:value-of select="$font" /> }
        h1 { 
            font-size: xx-large;
            font-weight: bold;
            border-bottom: 1px solid black; 
        }
        div.note {
            position: relative;
            display: block;
            padding: 5pt;
            margin: 5pt;
            white-space: -moz-pre-wrap; /* Mozilla */
            white-space: -pre-wrap; /* Opera 4 - 6 */
            white-space: -o-pre-wrap; /* Opera 7 */
            white-space: pre-wrap; /* CSS3 */
            word-wrap: break-word; /* IE 5.5+ */ 
        }
        table.note-info{
            margin: 20px 0 5px;
            font-size:xx-small;
            border-width: 0 1px 1px 0;
            border-style: solid;
        }
        table.note-info td{
            padding: 2px 5px;
            border-width: 1px 0 0 1px;
            border-style: solid;
        }
        /*//]]>*/
    </style>
</head>
<body>
    <xsl:for-each select="notes/tomboy:note">
    <div class="note">
        <xsl:apply-templates select="tomboy:text | tomboy:title"/>

        <table class="note-info" boder="0" cellpadding="0" cellspacing="0"
            summary="Note Info">
            <tr>
                <td>Last updated:</td>    
                <td><xsl:value-of select="tomboy:last-change-date" /></td>
            </tr>    
            <tr>
                <td>Tags:</td>    
                <td>
                    <xsl:for-each select="tomboy:tags">
                    <div><xsl:value-of select="tomboy:tag"/></div>    
                    </xsl:for-each>
                </td>
            </tr>    
        </table>
    </div>
    </xsl:for-each>
</body>
</html>
</xsl:template>



<xsl:template match="text()">
    
    <xsl:value-of select="."/>
</xsl:template>

<xsl:template match="child::tomboy:text">
    <xsl:apply-templates select="node()"/>
</xsl:template>

<xsl:template match="tomboy:title">
    <h1><xsl:value-of select="text()"/></h1>
</xsl:template>
<xsl:template match="tomboy:bold">
    <strong><xsl:apply-templates select="node()"/></strong>
</xsl:template>
<xsl:template match="tomboy:italic">
    <i><xsl:apply-templates select="node()"/></i>
</xsl:template>
<xsl:template match="tomboy:monospace">
    <code><xsl:apply-templates select="node()"/></code>
</xsl:template>

<xsl:template match="tomboy:strikethrough">
<strike><xsl:apply-templates select="node()"/></strike>
</xsl:template>

<xsl:template match="tomboy:highlight">
<span style="background:yellow"><xsl:apply-templates select="node()"/></span>
</xsl:template>

<xsl:template match="tomboy:datetime">
<span style="font-style:italic;font-size:small;color:#888A85">
<xsl:apply-templates select="node()"/>
</span>
</xsl:template>

<xsl:template match="size:small">
<span style="font-size:small"><xsl:apply-templates select="node()"/></span>
</xsl:template>

<xsl:template match="size:large">
<span style="font-size:large"><xsl:apply-templates select="node()"/></span>
</xsl:template>

<xsl:template match="size:huge">
<span style="font-size:xx-large"><xsl:apply-templates select="node()"/></span>
</xsl:template>

<xsl:template match="link:broken">
<span style="color:#555753;text-decoration:underline">
<xsl:value-of select="node()"/>
</span>
</xsl:template>
<xsl:template match="link:internal">
<a style="color:#204A87" href="#{node()}">
<xsl:value-of select="node()"/>
</a>
</xsl:template>

<xsl:template match="link:url">
<a style="color:#3465A4" href="{node()}"><xsl:value-of select="node()"/></a>
</xsl:template>

<xsl:template match="tomboy:list">
<ul>
<xsl:apply-templates select="tomboy:list-item" />
</ul>
</xsl:template>

<xsl:template match="tomboy:list-item">
<li>
<xsl:if test="normalize-space(text()) = ''">
<xsl:attribute name="style">list-style-type: none</xsl:attribute>
</xsl:if>
<xsl:attribute name="dir">
<xsl:value-of select="@dir"/>
</xsl:attribute>
<xsl:apply-templates select="node()" />
</li>
</xsl:template>

<xsl:template match="//tomboy:x | //tomboy:y | //tomboy:width | //tomboy:height | //tomboy:cursor-position | //tomboy:create-date | tomboy:last-change-date | tomboy:open-on-startup | tomboy:last-metadata-change-date | tomboy:tags">
<xsl:comment>literal</xsl:comment>
</xsl:template>

</xsl:stylesheet>

Usage

I saved the files here in ~/scripts/python/tomboy2html/. Thus, to save all the Tomboy notes in all_notes.xhtml and all_notes.html, I can issue the following:
$ chmod +x ~/scripts/python/tomboy2html/tomboy2html.py
$ ~/scripts/python/tomboy2html/tomboy2html.py -p "all_notes" -x -s \
~/scripts/python/tomboy2html/tomboy-notes.xsl
It creates all_notes.xhtml and all_notes.html files in current directory.

Update 09 February 2011

Clone the Git repo:
$ git clone git://github.com/rosmanov/Tomboy2HTML.git

17 December 2010

Anacron for a user

Normally, anacron is launched by root. And here is how a user can launch his/her own anacron jobs.

Prepare file and dirs

$ mkdir -p ~/etc/cron.daily ~/etc/cron.weekly ~/etc/cron.monthly \
~/var/spool/anacron
$ cp /etc/cron.daily/0anacron ~/etc/cron.daily/
$ cp /etc/cron.daily/.placeholder ~/etc/cron.daily/
$ cp /etc/cron.weekly/0anacron ~/etc/cron.weekly/
$ cp /etc/cron.weekly/.placeholder ~/etc/cron.weekly/
$ cp /etc/cron.monthly/0anacron ~/etc/cron.monthly/
$ cp /etc/cron.monthly/.placeholder ~/etc/cron.monthly/
$ find ~/etc/cron.* -name 0anacron -exec chmod u+x {} \;

~/etc/anacrontab

SHELL=/bin/sh
PATH=/usr/local/sbin:/usr/local/bin:/sbin:/bin:/usr/sbin:/usr/bin
MAILTO=user@domain.com
LOGNAME=anacron

1   5   cron.daily   nice run-parts --report /etc/cron.daily
7   10  cron.weekly  nice run-parts --report /etc/cron.weekly
@monthly    15  cron.monthly nice run-parts --report /etc/cron.monthly
Replace MAILTO value with your mailbox. If mailx package installed, you should receive anacron job error outputs by E-mail.

Make anacron launched on login

$ echo "
/usr/sbin/anacron -t \$HOME/etc/anacrontab -S \$HOME/var/spool/anacron
" >> ~/.bash_profile
UPDATE 20 DECEMBER 2010
Note, if ~/.profile exists, ~/.bash_profile may be ignored(as in my case). I wonder, why? Bash man page bash(1) claims:
When bash is invoked as an interactive login shell, or as a non-interactive shell with the --login option, it first reads and executes commands from the file /etc/profile, if that file exists. After reading that file, it looks for ~/.bash_profile, ~/.bash_login, and ~/.profile, in that order, and reads and executes commands from the first one that exists and is readable.
Put your scripts in ~/etc/cron.daily, ~/etc/cron.weekly and ~/etc/cron.monthly directories. Note, that script file names must consist entirely of upper and lower case letters, digits, underscores, and hyphens, since we launch them through run-parts(see man run-parts 8). In case of custom fienames you should apply --regex option.

25 November 2010

Shell script to back up SVN working copy changes

To copy items shown by svn status, one can use a simple script as following.

#!/bin/bash - 
set -o nounset                              # Treat unset variables as an error

# Displays the message, usage info and exits with error code 1
function my_usage()
{
    msg=$1
    [[ $msg ]] && echo $msg
    echo "Usage:
    ./$0 source_dir destination_dir
    source_dir          Source directory
    destination_dir         Destination directory"
    exit 1
}

# Returns dir name with trailing slash
function my_get_dirname()
{
    dir=$1
    if [[ ${dir:${#dir}-1:1} != '/' ]]; then 
        dir=$dir"/"
    fi
    echo $dir
}

src_dir=`my_get_dirname $1`
dst_dir=`my_get_dirname $2`
verbose=1

# Validate args
if [[ ! -d $src_dir ]]; then 
    usage "'$src_dir' is not a directory"
elif [[ ! -d $dst_dir  ]]; then 
    usage "'$dst_dir' is not a directory"
fi

# Remember current dir 
dir=`pwd`
cd $src_dir

# Loop through files and folders 
svn st | awk '{ print $2 }' | while read F
do
    if [[ $F != 'framework' ]]; then
        # Create directory, if not exists
        d=`dirname "$dst_dir$F"`
        if [[ ! -d $d ]]; then 
            mkdir -p "$d"
        fi

        # Copy file or directory
        if [[ -f $F ]]; then
            [[ $verbose = 1 ]] && echo "FILE $F"
            cp -f "$F" "$dst_dir$F"
        elif [[ -d $F ]]; then
            [[ $verbose = 1 ]] && echo "DIR $F"
            [[ ! -d "$dst_dir$F" ]] && mkdir -p "$dst_dir$F"
            cp -rf $F $dst_dir$F/../
        fi
    fi
done

# Go to the initial dir 
cd $dir

# vim: set textwidth=80:softtabstop=4:tabstop=4:shiftwidth=4:
# vim: set expandtab:autoindent:

11 November 2010

Enable/Disable Apache virtual hosts on Ubuntu with Zenity

Here is a simple bash script I use to toggle virtual hosts in a GUI window.

#!/bin/bash
# get available & enables site names & set corresp-ly checkboxes in the following zenity list
vh_avail=(`ls -B /etc/apache2/sites-available/`);

i=0
s=""

while (( i< ${#vh_avail[*]} ))
do
 if [ -f /etc/apache2/sites-enabled/${vh_avail[$i]} ] ; then 
  s=$s"TRUE "
 else 
  s=$s"FALSE "
 fi
 s=$s${vh_avail[$i]}" "
 
 (( ++i ))
done

# get vhost name

vhosts=`(zenity --list --width=700 --height=500 --title="Choose vhost" \
 --checklist --column="" --column="vhost" $s)`;

case $? in 
 0) # OK
   a=(`echo "$vhosts"|tr "\|" " "`);
   i=0;
   di=0;
   
   #disable all
   
   while (( i< ${#vh_avail[*]} ))
   do
    if [ -f /etc/apache2/sites-enabled/${vh_avail[$i]} ] ; then 
     a2dissite ${vh_avail[$i]}
    fi
    (( ++i ))
   done
   
   # enable needed
   i=0;   
   while ((i < ${#a[*]}))
   do 
    if [ -f /etc/apache2/sites-available/${a[$i]} ] ; then
     a2ensite ${a[i++]}
    else
     (( di++ ))
    fi
   done 
   
   info="${#a[*]} vhosts enabled";
   
   if (( di>0 ));then
    info="$info\n$di vhosts not found in /etc/apache2/sites-available/"
    zenity --warning --text="$info"
   else 
    zenity --info --text="$info"
   fi
   
   # reload apache
   /etc/init.d/apache2 restart #reload not always works
  ;;
 1) # Cancel
  exit 0
  ;; 
esac
So you can make a desktop launcher with command
gksudo /share/scripts/bash/vhosts_ed.sh

06 November 2010

How to convert MP3 file tag encodings in Ubuntu

It was somewhat troublesome for me to convert ID3 tags for large amount of files, especially in case of file names, with spaces and other "special" characters. I found tools and managed to successfully convert Windows-1251 MP3 file tags to UTF-8. I also post a sample bash script here.

mp3info

This tool displays and modifies ID3 tags. Quite simple in use. I would deal with it, if there were no problems with long character sequences(just cuts unpredictably!).

mid3iconv

It's job is exactly what I want. Converts ID3 tag encodings to Unicode. Usage is simplisity itself, e.g.
$ mid3iconv -e "Windows-1251" ~/Music/sample.mp3

Sample bash script

So the task is to iterate each file and apply mid3iconv. But one should care files already having Unicode tags. Therefore, the following script tries to detect encoding by means of enca tool.

#!/bin/bash 
# Splits the first argument by delimeter specified by the 2nd parameter 
# E.g. my_split_str $string $delim
# If delimeter is not specified, the func defaults to '='
# Requirements: 
# enca
# mp3info
# python-mutagen (mid3iconv)
#
# Example: bash mp3id3enc.sh --from-code=Windows-1251 --to-code=UTF-8
function my_split_str()
{
    if (( $# <=1 )) 
    then 
        IFS='=' 
    else 
        IFS=$2 
    fi 
    set -- $1
    echo $*
}

function usage()
{
    echo $0" [options]"
    echo "Options:
    --src-dir       Source dir
    --auto-detect-enc       Whether try to auto detect source encoding. Default: 0
    --from-code     Source text encoding. Default: Windows-1251
    --to-code       Target text encoding. Default: UTF-8
    --guess-lang        Language to use when guessing source text encoding. Default: ru
    "
    exit 1
}

# Set option defaults
src_dir='./'                # source directory
auto_detect_enc=0           # auto detect source encoding
from_code='Windows-1251'
to_code='UTF-8'
guess_lang='ru'

# Get options
for i in $*
do
  o=(`my_split_str $i '='`)
  case ${o[0]} in
    --src-dir)
        src_dir=${o[1]}
        # Add trailing slash, if not added yet
        if [[ ${src_dir#${src_dir%?}} != '/' ]]; then src_dir=$src_dir"/"; fi
      ;;
    --from-code)
      from_code=${o[1]}
      ;;
    --to-code)
      to_code=${o[1]}
      ;;
    --guess-lang)
      guess_lang=${o[1]}
      ;;
    --auto-detect-enc)
      auto_detect_enc=1
      ;;
    --help)
        usage
        exit 1
      ;;
    *)
      # unknown option
      echo "Unknown option ${o[0]}"
      usage
      exit 2
      ;;
  esac
done

find "$src_dir" -name "*.mp3" | while read FILENAME
do
    echo "$FILENAME..."

    # Try to detect encoding
    if [[ $auto_detect_enc = 1 ]]; then
        e=`mp3info -p "%t" "$FILENAME" | enca -gL $guess_lang`

        if [[ "$e" = *1251* ]]; then 
            from_code="Windows-1251"
        elif [[ "$e" = *CP866* || "$e" = *866* ]]; then
            from_code="CP866"
        elif [[ "$e" = *KOI8-R* ]]; then
            from_code="KOI8-R"
        else
            echo "couldn't detect encoding for "$FILENAME
            echo "$FILENAME ($e)" >> not-detected.log 
            continue
        fi

        echo "detected encoding: "$from_code
    fi

    t=`mp3info -p "%t" "$FILENAME" | enca -L ru`

    # For unrecognized encoding assume Windows-1251 
    if [[ $t = *Unrecognized* ]]; then 
        t="Windows-1251"
    fi
    echo "t='$t'"
    if [[ $t != *866* && $t != *1251* ]]; then 
        echo "$FILENAME skipped"
        continue
    fi

    mid3iconv -e "$from_code" "$FILENAME"

    echo
done
Notice, loop like

for FILENAME in $(find $src_dir -name "*.mp3"); do
...
done
fails with file names containing spaces and other escape sequences.