automated

hex2bin preserving endianness in C

I cannot help copying this snippet. Assume f is a (char *) of length L, containing an hex string like '0aabdda' (without the leading “0x”, like something coming from a sha function —or like the sha1 stored by Leopard in the password files, which is the origin of this problem). You want to transform it into the corresponding sequence of bytes (that is, assuming the string is of even length, otherwise, we add a trailing, yes, trailing, at the end, ’0′). We shall store the result in t, which points to a (char *) of length L/2.

The following C code does the trick: (first of all we must set t to 0);

int k = (L%2 ? L/2+1 : L/2);
memset(t, 0, L/2);
for(i=0; i<L; i++){
  t[i/2] += ((i%2) ? 1 : 16) *
    ((f[i] > 'F') ? (f[i] - 'a' + 10) :
      ((f[i] > '9') ? (f[i] - 'A' + 10) :
        (f[i] - '0')))
}

Thus, if f points to the string 100aff, t points to the sequence of bytes 16, 10, 255 after the loop.

The reverse operation is well known:

for(i=0; i<k; i++){
  sprintf(f+2*i, "%02X", t[i]);
}

I just don’t want to forget it.

scripts, shell

echo -n woes

It took me quite a while to realize that the following line does not do what you think it does:

$ echo '$1$CSmo96nX$G0PL/Cs/of5qDN2vMnyHp0' | openssl base64 | tr -d '\n'

You should always use the -n option if you want to make sure there is no spurious trailing newline:

$ echo -n '$1$CSmo96nX$G0PL/Cs/of5qDN2vMnyHp0' | openssl base64 | tr -d '\n'

(By the way, the encrypted message says just 'patata0' and it is not my password).

Or… is it?

Tested on two Linux systems (Fedora & Ubuntu) and one Snow Leopard.

shell

Setting variables in emacs at file header

When editing LaTeX files, I usually call the master file of a project 00father.ltx for historical reasons. Moreover, the following line is part of my .emacs:

(setq-default TeX-master "00father.ltx")

because most of the time I am editing multifile projects.

However, from time to time I need to write a single-file document and in this case, naming it 00father.ltx is not that useful, and I do not want to have to set the master-file variable each time I load the file.

There is an easy way to get this done. Just include a line at the top of the file -as a comment in the appropriate language- setting the variables. The syntax is as follows (in C, for example):

/* -*- variable1: value1; variable2: value2; -*- */

I am giving two examples. The first one in C again. Assume this is the header of a file called trial.c

/* *-* tab-width: 8; column-number-mode: 1; fill-column: 80; -*- */

The line tells emacs to set the length of a tab to 8 spaces (usual in BSD), to show the column number in the information line and to wrap lines (if wrapping -fill-mode- is set) at 80 characters.

For my LaTeX issue, the first line of a single-file document letter_to_my_friend.ltx goes as follows (notice the difference in the comment syntax):

% -*- TeX-master: "letter_to_my_friend.ltx"; -*-

I have checked and if your file is a shell script, which usually begins with

#!/bin/sh

(or some similar line), you can place the variable-setting line just afterwards.

shell

Reverting to a previous version with svn (rollback)

To rollback (that is, revert to a previous version) some files/dirs… using subversion, you need as Aral Balkan explains to

  • Merge the previous version
  • Commit

Like this (assumming you want to roll back from version 61 to 58):

pera $ svn merge r61:58 https://my.project.at.sourceforge/svnroot/project/dir1/src/
[... output ...]
pera $ svn ci -m "Reverted to version 58"

which is strange but works. Forget about the revert command, it has a different functionality.
You may want to run

pera $ svn merge --dry-run r61:58 ........

to check the changes which will take place before messing everything up.

shell

IPCS

ipcs shows the status of SYSV inter process communication facilities.

$ ipcs -s

------ Semaphore Arrays --------
key        semid      owner      perms      nsems
0xdd3adabd 0          fernape    600        1         

I had forgotten about this command but I remembered it when we ran out of semaphores in our Linux system two days ago.

Other way of getting the same information is to cat the following files:

$ ls /proc/sysvipc
msg        sem        shm

Enjoy!

scripts

Setting a Timer

Sometimes I need a timer to focus on something and to alert me when to stop. Remember, we are real commandliners, so we do not want those fancy applications with a lot of features, we need a script ;-) so here it is:

#!/bin/bash

usage() {
  name=`basename $0`
  echo "Usage: $name hh:mm:ss"
  echo "Example: $name \"00:15:30\""
}

if [ $# != 1 ]
then
  usage
  exit
fi

IFS=:
set -- $*
secs=$(( ${1#0} * 3600 + ${2#0} * 60 + ${3#0} ))
while [ $secs -gt 0 ]
do
  sleep 1 &
  printf "\r%02d:%02d:%02d" $((secs/3600)) $(((secs/60)%60)) $((secs%60))
  secs=$(( $secs - 1 ))
  wait
done
echo

It works in any POSIX shell.

I was writing one but I found this thread of the UNIX and linux forum where the user cfajohnson solves it in a better way.

The code:

sleep 1 &
...
wait

minimizes the skew of the loop, so every cycle is as close to 1 second as possible.

I have not included the final beep in the script. You can do it with the usual:

printf ("\a")

which works on all unix-like systems.

In linux you can use the beep command that allows you to control pitch, duration, and repetitions, for example:

$ beep -f 300.7 -r 2 -d 100 -l 400

makes 2 repetitions of a beep at 300.7 Hz for 400 milliseconds and a delay between repetitions of 100 milliseconds.

In OS X you can play with the say command:

$ say "You deserve some rest"

network

Downloading a file with wget through a specific interface

Sometimes I need to download a file from a computer with multiple net interfaces, but only one is connected to the Internet. In these cases I use the bind-address option of the wget command, which binds the connection to the address specified in the local machine.

$ wget --bind-address=192.168.213.141 \
> ftp://ftp.icm.edu.pl/vol/rzm1/linux-fedora-secondary/development/source/SRPMS/ppp-2.4.4-11.fc11.src.rpm
--2010-03-27 12:25:56--  ftp://ftp.icm.edu.pl/vol/rzm1/linux-fedora-secondary/development/source/SRPM/ppp-2.4.4-11.fc11.src.rpm
           => `ppp-2.4.4-11.fc11.src.rpm.1'
Resolving ftp.icm.edu.pl... 193.219.28.140
Connecting to ftp.icm.edu.pl|193.219.28.140|:21... connected.
Logging in as anonymous ... Logged in!
==> SYST ... done.    ==> PWD ... done.
==> TYPE I ... done.  ==> CWD /vol/rzm1/linux-fedora-secondary/development/source/SRPMS ... done.
==> SIZE ppp-2.4.4-11.fc11.src.rpm ... 724348
==> PASV ... done.    ==> RETR ppp-2.4.4-11.fc11.src.rpm ... done.
Length: 724348 (707K)

100%[========================================>] 724,348      173K/s   in 4.1s    

2010-03-27 12:26:03 (173 KB/s) - `ppp-2.4.4-11.fc11.src.rpm.1' saved [724348]

shell

Shell scripting for Nautilus

It has been a while since I wrote my last post. Sorry for the delay, but I was a bit busy lately. In this post, I shall explain how to get the most of your nautilus file manager by using shell scripts.

Nautilus provides some facilities available from shell scripts. Combining them with a small utility called zenity can improve your daily tasks.

Nautilus has the ability of executing shell scripts applying them to the selected files. The executable scripts are those present in the following directory:

~/.gnome2/nautilus-scripts

Every script present in that directory will be shown in the “Scripts” entry of the contextual menu that shows up by right-clicking on a file. If that entry is not shown, navigate into the directory mentioned above in Nautilus and you will see a message indicating that all the scripts in the folder will be available from now on.
Nautilus sets up four environment variables for the process executing the script which are:

NAUTILUS_SCRIPT_SELECTED_FILE_PATHS: selected file paths.

NAUTILUS_SCRIPT_SELECTED_URIS: URIs delimited by \n

NAUTILUS_SCRIPT_CURRENT_URI: Current URI

NAUTILUS_SCRIPT_WINDOW_GEOMETRY: size and location of the current window

In addition, the names (without the full path) of all the selected items are passed to the script as input parameters.

These scripts will be executed silently (not in a terminal window) so it seems reasonable to be able to communicate with the user. Enter zenity.

Introducing zenity
zenity is a program for displaying GTK+ dialogs. It reads data from stdin and returns user input through stdou. It is an easy way of gathering and showing information from a shell script on a desktop environment. Here are some examples on how to use zenity:

Show an information window:

$ zenity --info --title=InfoWindow --text="This is an information text"

zenity-info-window

Get some user input:

$zenity --entry --title=EntryWindow --text="Type a text, please"

zenity-entry-window

Hello World!
$

As you can see, zenity provides an easy way to interact with the user in a graphical environment.

With this little information, it is possible to write a small debugging script. I called it test.sh

#! /bin/bash
# Use /usr/local/bin/bash on FreeBSD

ZENITY=$(which zenity)

$ZENITY --info --text="$*"

$ZENITY --info --text="NAUTILUS_SCRIPT_SELECTED_FILE_PATHS = $NAUTILUS_SCRIPT_SELECTED_FILE_PATHS"
$ZENITY --info --text="NAUTILUS_SCRIPT_SELECTED_URIS = $NAUTILUS_SCRIPT_SELECTED_URIS"
$ZENITY --info --text="NAUTILUS_SCRIPT_CURRENT_URI  = $NAUTILUS_SCRIPT_CURRENT_URI"
$ZENITY --info --text="NAUTILUS_SCRIPT_WINDOW_GEOMETRY = $NAUTILUS_SCRIPT_WINDOW_GEOMETRY"

This script shows the contents of the parameters and the Nautilus variables.

zenity is really easy to use, and with a little effort and some bash scripting knowledge (that I will not explain, because it is out of the scope of this post) you can write a convenience script like the one below (I named it packit.sh):

#! /bin/bash
# Use /usr/local/bin/bash on FreeBSD
#
# Small script intended to be used from Nautilus.
# It compresses the selected files and/or directories
# in several formats
# Requires: zenity, tar, gzip, bzip2, zip, mkisofs

FORMATS="tar tgz tbz2 zip iso9660"
ZENITY=$(which zenity)

# Check if we have selected any files...
if [ -z "$NAUTILUS_SCRIPT_SELECTED_FILE_PATHS" ]; then
        $ZENITY --error --text="No files selected"
        exit 0;
fi

# Ask for output file name
output_filename=packit-$(date +%s)
output_filename=$($ZENITY --entry --title="Output file name" --text="Type file name" --entry-text="$output_filename")

# Ask the user to select a compressing format
selected_format=$($ZENITY --title="Select format" --list --column=Format $FORMATS)

if [ -z "$selected_format" ]; then
        $ZENITY --error --text="No format selected"
        exit 0;
fi

# Select the compressing utility to use
case $selected_format in
        "tar")
                COMP_COMMAND="tar cvf"
        ;;
        "tgz")
                COMP_COMMAND="tar czvf"
        ;;
        "tbz2")
                COMP_COMMAND="tar cjvf"
        ;;
        "zip")
                COMP_COMMAND="zip -9"
        ;;
        "iso9660")
                COMP_COMMAND="mkisofs -R -o"
        ;;
esac

# Execute the command while showing a progress bar.
(echo "0" ;
 $COMP_COMMAND "$output_filename" $NAUTILUS_SCRIPT_SELECTED_FILE_PATHS
 echo "100") | $ZENITY --progress --pulsate --title=Working...

The script above works for me, but it assumes the existence of several commands. If you want to use it you might need modify some lines.

Enjoy!

shell

Running e2fsck on a mounted filesystem

I know, running fsck on a mounted filesystem is utterly unrecommended. The command warns you (it actually frightens you) with the following message:

# fsck /dev/VolGroup00/LogVol00
fsck 1.41.4 (27-Jan-2009)
e2fsck 1.41.4 (27-Jan-2009)
/dev/VolGroup00/LogVol00 is mounted.  

WARNING!!!  Running e2fsck on a mounted filesystem may cause
SEVERE filesystem damage.

Do you really want to continue (y/n)? no

check aborted.

But sometimes I need to check a filesystem in a remote host, so I cannot boot from a liveCD to run fsck in the unmounted device. Looking for an option allowing me to overcome this nuisance I found the following in e2fsck’s man page:

Note  that  in general it is not safe to run e2fsck on mounted filesys-
tems.  The only exception is if the -n option is specified, and -c, -l,
or  -L  options  are not specified.

Using e2fsck instead of fsck is not a problem because it checks ext2 and ext3 filesystems and mine are ext3 (fsck checks and optionally repairs a lot of filesystem types).

Let us see what the man page says about the -n option:

-n     Open the filesystem read-only, and assume an answer of  `no'  to
       all  questions.   Allows  e2fsck  to  be used non-interactively.
       (Note: if the -c, -l, or -L options are specified in addition to
       the -n option, then the filesystem will be opened read-write, to
       permit the bad-blocks list to be  updated.   However,  no  other
       changes will be made to the filesystem.)  This option may not be
       specified at the same time as the -p or -y options.

So it seems to be safe running it with that option. But the e2fsck man page also states (dealing with the safe check):

However, even if it is safe to do
so, the results printed by e2fsck are not valid if  the  filesystem  is
mounted.    If e2fsck asks whether or not you should check a filesystem
which is mounted, the only correct answer is ``no''.  Only experts  who
really know what they are doing should consider answering this question
in any other way.

So, I use it only when I want to know if there is something wrong with the filesystem. I run it with the -f option too which forces checking even if the file system seems clean.

# e2fsck -fn /dev/VolGroup00/LogVol00
e2fsck 1.41.4 (27-Jan-2009)
Warning!  /dev/VolGroup00/LogVol00 is mounted.
Warning: skipping journal recovery because doing a read-only filesystem check.
Pass 1: Checking inodes, blocks, and sizes
Pass 2: Checking directory structure
Pass 3: Checking directory connectivity
Pass 4: Checking reference counts
Pass 5: Checking group summary information
F10-i686-Live: 123335/475136 files (3.8% non-contiguous), 876070/1900544 blocks

If the output is like above all is OK, but if the device has errors, then you will need to run fsck with the filesystem unmounted.

# touch /forcefsck
Creating an empty forcefsck file in the root directoy will force fsck to run at the next boot.
02.23.10 | rafacas | # | (0)