Customizing the User Environment in BSD

If you use an open source operating system, you probably have a few favorite hacks that you like to apply to make things run more smoothly. This article, the first of three parts, focuses on some good hacks for customizing the user environment. It is excerpted from chapter one of the book BSD Hacks, written by Dru Lavigne (Copyright © 2005 O’Reilly Media, Inc. All rights reserved. Used with permission from the publisher. Available from booksellers or direct from O’Reilly Media; ISBN: 0596006799).

Users of open source (http://opensource.org) Unix operating systems are an interesting breed. They like to poke under the surface of things, to find out how things work, and to figure out new and interesting ways of accomplish ing common computing tasks. In short, they like to “hack.”

While this book concentrates on the BSDs, many of the hacks apply to any open source operating system. Each hack is simply a demonstration of how to examine a common problem from a slightly different angle. Feel free to use any of these hacks as a springboard to your own customized solution. If your particular operating system doesn’t contain the tool used in the solution, use a tool that does exist, or invent your own!

This chapter provides many tools for getting the most out of your working environment. You’ll learn how to make friends with your shell and how to perform your most common tasks with just a few keystrokes or mouse clicks. You’ll also uncover tricks that can help prevent command-line disasters. And, above all, you’ll discover that hacking BSD is fun. So, pull your chair up to your operating system of choice and let’s start hacking.

Hack 1: Get the Most Out of the Default Shell

Become a speed daemon at the command line.

For better or for worse, you spend a lot of time at the command line. If you’re used to administering a Linux system, you may be dismayed to learn that bash is not the default shell on a BSD system, for either the superuser or regular user accounts.

Take heart; the FreeBSD superuser’s default tcsh shell is also brimming with shortcuts and little tricks designed to let you breeze through even the most tedious of tasks. Spend a few moments learning these tricks and you’ll feel right at home. If you’re new to the command line or consider yourself a terrible typist, read on. Unix might be a whole lot easier than you think.

NetBSD and OpenBSD also ship with the C shell as their default shell. However, it is not always the same tcsh, but often its simpler variant, csh, which doesn’t support all of the tricks provided in this hack.

However, both NetBSD and OpenBSD provide a tcsh package in their respective package collections.

History and Auto-Completion

I hate to live without three keys: up arrow, down arrow, and Tab. In fact, you can recognize me in a crowd, as I’m the one muttering loudly to myself if I’m on a system that doesn’t treat these keys the way I expect to use them.

tcsh uses the up and down arrow keys to scroll through your command history. If there is a golden rule to computing, it should be: “You should never have to type a command more than once.” When you need to repeat a command, simply press your up arrow until you find the desired command. Then, press Enter and think of all the keystrokes you just saved yourself. If your fingers fly faster than your eyes can read and you whiz past the right command, simply use the down arrow to go in the other direction.

The Tab key was specifically designed for both the lazy typist and the terrible speller. It can be painful watching some people type out a long command only to have it fail because of a typo. It’s even worse if they haven’t heard about history, as they think their only choice is to try typing out the whole thing all over again. No wonder some people hate the command line!

Tab activates auto-completion. This means that if you type enough letters of a recognizable command or file, tcsh will fill in the rest of the word for you. However, if you instead hear a beep when you press the Tab key, it means that your shell isn’t sure what you want. For example, if I want to run sockstat and type:

  % so

then press my Tab key, the system will beep because multiple commands start with so. However, if I add one more letter:

   % soc

and try again, the system will fill in the command for me:

    % sockstat

Editing and Navigating the Command Line

There are many more shortcuts that can save you keystrokes. Suppose I’ve just finished editing a document. If I press my up arrow, my last command will be displayed at the prompt:

  % vi mydocs/today/verylongfilename

I’d now like to double-check how many words and lines are in that file by running this command:

   % wc mydocs/today/verylongfilename

I could pound on the backspace key until I get to the vi portion of the command, but it would be much easier to hold down the Ctrl key and press a . That would bring me to the very beginning of that command so I could replace the vi with wc . For a mnemonic device, remember that just as a is the first letter of the alphabet, it also represents the first letter of the command at a tcsh prompt.

I don’t have to use my right arrow to go to the end of the command in order to press Enter and execute the command. Once your command looks like it should, you can press Enter. It doesn’t matter where your cursor happens to be.

Sometimes you would like your cursor to go to the end of the command. Let’s say I want to run the word count command on two files, and right now my cursor is at the first c in this command:

  % wc mydocs/today/verylongfilename

If I hold down Ctrl and press e , the cursor will jump to the end of the command, so I can type in the rest of the desired command. Remember that e is for end.

Finally, what if you’re in the middle of a long command and decide you’d like to start from scratch, erase what you’ve typed, and just get your prompt back? Simply hold down Ctrl and press u for undo.

If you work in the Cisco or PIX IOS systems, all of the previous tricks work at the IOS command line.

Did you know that the cd command also includes some built-in shortcuts? You may have heard of this one: to return to your home directory quickly, simply type:

  % cd

That’s very convenient, but what if you want to change to a different previous directory? Let’s say that you start out in the /usr/share/doc/en_US. ISO8859-1/books/handbook directory, then use cd to change to the /usr/ X11R6/etc/X11 directory. Now you want to go back to that first directory. If you’re anything like me, you really don’t want to type out that long directory path again. Sure, you could pick it out of your history, but chances are you originally navigated into that deep directory structure one directory at a time. If that’s the case, it would probably take you longer to pick each piece out of the history than it would be to just type the command manually.

Fortunately, there is a very quick solution. Simply type:

  % cd -

Repeat that command and watch as your prompt changes between the first and the second directory. What, your prompt isn’t changing to indicate your current working directory? Don’t worry, “Useful tcsh Shell Configuration File Options” [Hack #2] will take care of that.

Learning from Your Command History

Now that you can move around fairly quickly, let’s fine-tune some of these hacks. How many times have you found yourself repeating commands just to alter them slightly? The following scenario is one example.

Remember that document I created? Instead of using the history to bring up my previous command so I could edit it, I might have found it quicker to type this:

  % wc !$
  wc mydocs/today/verylongfilenam e 
       19         97        620 mydocs/today/verylongfilename
 

The !$ tells the shell to take the last parameter from the previous command. Since that command was:

  % vi mydocs/today/verylongfilename

it replaced the !$ in my new command with the very long filename from my previous command.

The ! (or bang!) character has several other useful applications for dealing with previously issued commands. Suppose you’ve been extremely busy and have issued several dozen commands in the last hour or so. You now want to repeat something you did half an hour ago. You could keep tapping your up arrow until you come across the command. But why search yourself when ! can search for you?

For example, if I’d like to repeat the command mailstats , I could give ! enough letters to figure out which command to pick out from my history:

  $ !ma

 ! will pick out the most recently issued command that begins with ma . If I had issued a man command sometime after mailstats command, tcsh would find that instead. This would fix it though:

  % !mai

If you’re not into trial and error, you can view your history by simply typing:

  % history

If you’re really lazy, this command will do the same thing:

   % h

Each command in this history will have a number. You can specify a command by giving ! the associated number. In this example, I’ll ask tcsh to reissue the mailstats command:

  % h
    165  16:51 mailstats
    166  16:51 sockstat
    167  16:52 telnet localhost 25
    168  16:54 man sendmail 
  % !165

Silencing Auto-Complete

The last tip I’ll mention is for those of you who find the system bell irritat ing. Or perhaps you just find it frustrating typing one letter, tabbing, typing another letter, tabbing, and so on until auto-complete works. If I type:

  % ls -l b

 then hold down the Ctrl key while I press d :

  backups/ bin/ book/ boring.jpg
  ls -l b

I’ll be shown all of the b possibilities in my current directory, and then my prompt will return my cursor to what I’ve already typed. In this example, if I want to view the size and permissions of boring.jpg, I’ll need to type up to here:

  % ls -l bor

before I press the Tab key. I’ll leave it up to your own imagination to decide what the d stands for.

See Also

  • man tcsh
{mospagebreak title=Hack 2: Useful tcsh Shell Configuration File Options}

Make the shell a friendly place to work in.

Now that you’ve had a chance to make friends with the shell, let’s use its configuration file to create an environment you’ll enjoy working in. Your prompt is an excellent place to start.

Making Your Prompt More Useful

The default tcsh prompt displays % when you’re logged in as a regular user and hostname# when you’re logged in as the superuser. That’s a fairly useful way to figure out who you’re logged in as, but we can do much better than that.

Each user on the system, including the superuser, has a .cshrc file in his home directory. Here are my current prompt settings:

  dru@~:grep prompt ~/.cshrc
 
if ($?prompt) then
          set prompt = "
%B%n@%~%b: "

That isn’t the default tcsh prompt, as I’ve been using my favorite customized prompt for the past few years. The possible prompt formatting sequences are easy to understand if you have a list of possibilities in front of you. That list is buried deeply within man cshrc , so here’s a quick way to zero in on it:

  dru@~:man cshrc
  /prompt may include

Here I’ve used the / to invoke the manpage search utility. The search string prompt may include brings you to the right section, and is intuitive enough that even my rusty old brain can remember it.

If you compare the formatting sequences shown in the manpage to my prompt string, it reads as follows:

  set prompt = "%B%n@%~%b: "

That’s a little dense. Table 1-1 dissects the options.

Table 1-1. Prompt characters

Character

Explanation

"

Starts the prompt string.

%B

Turns on bold.

%n

Shows the login name in the prompt.

@

I use this as a separator to make my prompt more visually appealing.

Table 1-1. Prompt characters (continued)

Character

Explanation

%~

Shows the current working directory. It results in a shorter prompt than %/, as my home directory is shortened from /usr/home/myusername to ~

%b

Turns off bold.

:

Again, this is an extra character I use to separate my prompt from the cursor.

"

Ends the prompt string.

With this prompt, I always know who I am and where I am. If I also needed to know what machine I was logged into (useful for remote administration), I could also include %M or %m somewhere within the prompt string.

The superuser’s .cshrc file (in /root, the superuser’s home directory has an identical prompt string. This is very fortunate, as it reveals something you might not know about the su command, which is used to switch users. Right now I’m logged in as the user dru and my prompt looks like this:

  dru@/usr/ports/net/ethereal:

Watch the shell output carefully after I use su to switch to the root user:

  dru@/usr/ports/net/ethereal: su
 
Password:
  dru@/usr/ports/net/ethereal:

Things seem even more confusing if I use the whoami command:

  dru@/usr/ports/net/ethereal: whoami
 
dru

However, the id command doesn’t lie:

  dru@/usr/ports/net/ethereal: id
 
uid=0(root) gid=0(wheel) groups=0(wheel), 5(operator)

It turns out that the default invocation of su doesn’t actually log you in as the superuser. It simply gives you superuser privileges while retaining your original login shell.

If you really want to log in as the superuser, include the login (-l) switch:

  dru@/usr/ports/net/ethereal: su -l 
  Password:
  root@~: whoami
 
root
  root@~: id
 
uid=0(root) gid=0(wheel) groups=0(wheel), 5(operator)

I highly recommend you take some time to experiment with the various for matting sequences and hack a prompt that best meets your needs. You can add other features, including customized time and date strings and command history numbers [Hack #1], as well as flashing or underlining the prompt.

Setting Shell Variables

Your prompt is an example of a shell variable. There are dozens of other shell variables you can set in .cshrc. My trick for finding the shell variables section in the manpage is:

  dru@~:man cshrc
  /variables described

As the name implies, shell variables affect only the commands that are built into the shell itself. Don’t confuse these with environment variables, which affect your entire working environment and every command you invoke.

If you take a look at your ~/.cshrc file, environment variables are the ones written in uppercase and are preceded with the setenv command. Shell variables are written in lowercase and are preceded with the set command.

You can also enable a shell variable by using the set command at your command prompt. (Use unset to disable it.) Since the variable affects only your current login session and its children, you can experiment with setting and unsetting variables to your heart’s content. If you get into trouble, log out of that session and log in again.

If you find a variable you want to keep permanently, add it to your ~/.cshrc file in the section that contains the default set commands. Let’s take a look at some of the most useful ones.

If you enjoyed Ctrl-d from “Get the Most Out of the Default Shell” [Hack #1], you’ll like this even better:

  set autolist

Now whenever you use the Tab key and the shell isn’t sure what you want, it won’t beep at you. Instead, the shell will show you the applicable possibilities. You don’t even have to press Ctrl-d first!

The next variable might save you from possible future peril:

  set rmstar

I’ll test this variable by quickly making a test directory and some files:

  dru@~:mkdir test
  dru@~:cd test
  dru@~/test:touch a b c d e
                 
 

Then, I’ll try to remove the files from that test directory:

  dru@~/test:rm *
 Do you really want to delete all files? [n/y]

Since my prompt tells me what directory I’m in, this trick gives me one last chance to double-check that I really am deleting the files I want to delete.

If you’re prone to typos, consider this one:

  set correct=all

This is how the shell will respond to typos at the command line:

  dru@~:cd /urs/ports 
  CORRECT>cd /usr/ports (y|n|e|a)?

Pressing y will correct the spelling and execute the command. Pressing n will execute the misspelled command, resulting in an error message. If I press e , I can edit my command (although, in this case, it would be much quicker for the shell to go with its correct spelling). And if I completely panic at the thought of all of these choices, I can always press a to abort and just get my prompt back.

If you like to save keystrokes, try:

  set implicitcd

You’ll never have to type cd again. Instead, simply type the name of the directory and the shell will assume you want to go there.

{mospagebreak title=Hack 3: Create Shell Bindings}

Train your shell to run a command for you whenever you press a mapped key.

Have you ever listened to a Windows power user expound on the joys of hotkeys? Perhaps you yourself have been known to gaze wistfully at the extra buttons found on a Microsoft keyboard. Did you know that it’s easy to configure your keyboard to launch your most commonly used applications with a keystroke or two?

One way to do this is with the bindkey command, which is built into the tcsh shell. As the name suggests, this command binds certain actions to
cer tain keys. To see your current mappings, simply type bindkey . The output is several pages long, so I’ve included only a short sample. However, you’ll recognize some of these shortcuts from “Get the Most Out of the Default Shell” [Hack #1].

  Standard key binding s
  "^A"          -> beginning-of-line
  "^B"          -> backward-char
  "^E"          -> end-of-line
  "^F"          -> forward-char
  "^L"          -> clear-screen
  "^N"          -> down-history
  "^P"          -> up-history
  "^U"          -> kill-whole-line
 
Arrow key bindings
  down          -> history-search-forward 
  up            -> history-search-backward
  left          -> backward-char
  right         -> forward-char
  home          -> beginning-of-line
  end           -> end-of-line

The ^ means hold down your Ctrl key. For example, press Ctrl and then l , and you’ll clear your screen more quickly than by typing clear . Notice that it doesn’t matter if you use the uppercase or lowercase letter.

Creating a Binding

One of my favorite shortcuts isn’t bound to a key by default: complete-word-fwd . Before I do the actual binding, I’ll first check which keys are available:

  dru@~:bindkey | grep undefined
 
"^G"           -> is undefined
  "305"         -> is undefined
  "307"         -> is undefined
  <snip>

Although it is possible to bind keys to numerical escape sequences, I don’t find that very convenient. However, I can very easily use that available Ctrl g. Let’s see what happens when I bind it:

  dru@~:bindkey "^G" complete-word-fwd

When I typed in that command, I knew something worked because my prompt returned silently. Here’s what happens if I now type ls -l /etc/, hold down the Ctrl key, and repeatedly press g :

  ls -l /etc/COPYRIGH T
  ls -l /etc/X11
  ls -l /etc/aliases
  ls -l /etc/amd.map

I now have a quick way of cycling through the files in a directory until I find the exact one I want. Even better, if I know what letter the file starts with, I can specify it. Here I’ll cycle through the files that start with a :

  ls -l /etc/a
  ls -l /etc/aliases
 
ls -l /etc/amd.map
  ls -l /etc/apmd.conf
  ls -l /etc/auth.conf
  ls -l /etc/a

Once I’ve cycled through, the shell will bring me back to the letter a and beep.

If you prefer to cycle backward, starting with words that begin with z instead of a , bind your key to complete-word-back instead.

When you use bindkey , you can bind any command the shell understands to any understood key binding. Here’s my trick to list the commands that tcsh understands:

  dru@~ man csh
  /command is bound

And, of course, use bindkey alone to see the understood key bindings. If you just want to see the binding for a particular key, specify it. Here’s how to see the current binding for Ctrl-g:

  dru@~:bindkey "^G"
 
"^G"  -> complete-word-fw d

Specifying Strings

What’s really cool is that you’re not limited to just the commands found in man csh . The s switch to bindkey allows you to specify any string. I like to bind the lynx web browser to Ctrl-w:

  dru@~:bindkey -s "^W" "lynxn"

I chose w because it reminds me of the World Wide Web. But why did I put n after the lynx ? Because that tells the shell to press Enter for me. That means by simply pressing Ctrl-w, I have instant access to the Web.

Note that I overwrite the default binding for Ctrl-w. This permits you to make bindings that are more intuitive and useful for your own purposes. For example, if you never plan on doing whatever ^J does by default, simply bind your desired command to it.

There are many potential key bindings, so scrolling through the output of bindkeys can be tedious. If you only stick with “Ctrl letter” bindings, though, it’s easy to view your customizations with the following command:

  dru@~:bindkey | head -n 28

As with all shell modifications, experiment with your bindings first by using bindkey at the command prompt. If you get into real trouble, you can always log out to go back to the defaults. However, if you find some bindings you want to keep, make them permanent by adding your bindkey statements to your .cshrc file. Here is an example:

  dru@~:cp ~/.cshrc ~/.cshrc.orig  
  dru@~:echo ‘bindkey "^G" complete-word-fwd’ >> ~/.cshrc

Notice that I backed up my original .cshrc file first, just in case my fingers slip on the next part. I then used >> to append the echoed text to the end of .cshrc. If I’d used > instead, it would have replaced my entire .cshrc file with just that one line. I don’t recommend testing this on any file you want to keep.

Along those lines, setting:

  set noclobber

will prevent the shell from clobbering an existing file if you forget that extra > in your redirector. You’ll know you just prevented a nasty accident if you get this error message after trying to redirect output to a file:

  .cshrc: File exists.

See Also

  • man tcsh 
  •  “Useful tcsh Shell Configuration File Options”
     [Hack #2]

{mospagebreak title=Hack 4: Use Terminal and X Bindings}

Take advantage of your terminal’s capabilities.

It’s not just the tcsh shell that is capable of understanding bindings. Your FreeBSD terminal provides the kbdcontrol command to map commands to your keyboard. Unfortunately, neither NetBSD nor OpenBSD offer this fea ture. You can, however, remap your keyboard under X, as described later.

Creating Temporary Mappings

Let’s start by experimenting with some temporary mappings. The syntax for mapping a command with kbdcontrol is as follows:

  kbdcontrol -f number "command"

Table 1-2 lists the possible numbers, each with its associated key combination.

Table 1-2. Key numbers

Number

Key combination

1, 2, . . . 12

F1, F2, . . . F12

13, 14, . . . 24

Shift+F1, Shift+F2, . . . Shift+F12

25, 26, . . . 36

Ctrl+F1, Ctrl+F2,. . . Ctrl+F12

Table 1-2. Key numbers (continued)

Number

Key combination

37, 38, . . . 48

Shift+Ctrl+F1, Shift+Ctrl+F2, . . . Shift+Ctrl+F12

49

Home

50

Up arrow

51

Page Up

52

Numpad – (Num Lock off)

53

Left arrow (also works in editor)

54

Numpad 5 (without Num Lock)

55

Right arrow

56

Numpad + (without Num Lock)

57

End

58

Down arrow (affects c history)

59

Page Down

60

Ins

61

Del

62

Left GUI key (Windows icon next to left Ctrl)

63

Right GUI key (Windows icon next to right Alt)

64

Menu (menu icon next to right Ctrl)

Those last three key combinations may or may not be present, depending upon your keyboard. My Logitech keyboard has a key with a Windows icon next to the left Ctrl key; that is the left GUI key. There’s another key with a Windows icon next to my right Alt key; this is the right GUI key. The next key to the right has an icon of a cursor pointing at a square containing lines; that is the Menu key.

Now that we know the possible numbers, let’s map lynx to the Menu key:

  % kbdcontrol -f 64 "lynx"

Note that the command must be contained within quotes and be in your path. (You could give an absolute path, but there’s a nasty limitation coming up soon.)

If I now press the Menu key, lynx is typed to the terminal for me. I just need to press Enter to launch the browser. This may seem a bit tedious at first, but it is actually quite handy. It can save you from inadvertently launching the wrong application if you’re anything like me and tend to forget which commands you’ve mapped to which keys.

Let’s see what happens if I modify that original mapping somewhat:

  % kbdcontrol -f 64 "lynx www.google.ca"
  kbdcontrol: function key string too long (18 > 16)

When doing your own mappings, beware that the command and its argu ments can’t exceed 16 characters. Other than that, you can pretty well map any command that strikes your fancy.

Shell Bindings Versus Terminal Bindings

Before going any further, I’d like to pause a bit and compare shell-specific bindings, which we saw in “Create Shell Bindings” [Hack #3], and the terminal-specific bindings we’re running across here.

One advantage of using kbdcontrol is that your custom bindings work in any terminal, regardless of the shell you happen to be using. A second advantage is that you can easily map to any key on your keyboard. Shell mappings can be complicated if you want to map them to anything other than “Ctrl letter”.

However, the terminal mappings have some restrictions that don’t apply to the tcsh mappings. For example, shell mappings don’t have a 16 character restriction, allowing for full pathnames. Also, it was relatively easy to ask the shell to press Enter to launch the desired command.

Terminal bindings affect only the current user’s terminal. Any other users who are logged in on different terminals are not affected. However, if the mappings are added to rc.conf (which only the superuser can do), they will affect all terminals. Since bindings are terminal specific, even invoking su won’t change the behavior, as the user is still stuck at the same terminal.

More Mapping Caveats

There are some other caveats to consider when choosing which key to map. If you use the tcsh shell and enjoy viewing your history [Hack #1], you’ll be disappointed if you remap your up and down arrows. The right and left arrows can also be problematic if you use them for navigation, say, in a text editor. Finally, if you’re physically sitting at your FreeBSD system, F1 through F8 are already mapped to virtual terminals and F9 is mapped to your GUI terminal. By default, F10 to F12 are unmapped.

If you start experimenting with mappings and find you’re stuck with one you don’t like, you can quickly return all of your keys to their default mappings with this command:

  % kbdcontrol -F

On the other hand, if you find some new mappings you absolutely can’t live without, make them permanent. If you have superuser privileges on a FreeBSD system you physically sit at, you can carefully add the mappings to /etc/rc.conf. Here, I’ve added two mappings. One maps lynx to the Menu key and the other maps startx to the left GUI key:

  keychange="64 lynx"
 
keychange="62 startx"

Since the superuser will be setting these mappings, the mapped keys will affect all users on that system. If you want to save your own personal mappings, add your specific kbdcontrol commands to the end of your shell configuration file. For example, I’ve added these to the very end of my ~/.cshrc file, just before the last line which says endif :

  % kbdcontrol -f 64 "lynx"
  % 
kbdcontrol -f 62 "startx"

Making Mappings Work with X

This is all extremely handy, but what will happen if you try one of your newly mapped keys from an X Window session? You can press that key all you want, but nothing will happen. You won’t even hear the sound of the system bell beeping at you in protest. This is because the X protocol handles all input and output during an X session.

You have a few options if you want to take advantage of keyboard bindings while in an X GUI. One is to read the documentation for your particular window manager. Most of the newer window managers provide a point and click interface to manage keyboard bindings. My favorite alternative is to try the xbindkeys_config application, which is available in the ports collection [Hack #84]:

  # cd /usr/ports/x11/xbindkeys_config 
  #
make install clean

This port also requires xbindkeys:

  # cd /usr/ports/x11/xbindkeys
  # make install clean

Rather than building both ports, you could instead add this line to /usr/ports/x11/xbindkeys_config/Makefile:

   BUILD_DEPENDS= xbindkeys:${PORTSDIR}/x11/xbindkeys

This will ask the xbindkeys_config build to install both ports.

Once your builds are complete, open an xterm and type:

  % xbindkeys –defaults  
~/.xbindkeysrc
  
  %
xbindkeys_config

The GUI in Figure 1-1 will appear.


Figure 1-1.  The xbindkeys_config program

Creating a key binding is a simple matter of pressing the New button and typing a useful name into the Name: section. Then, press Get Key and a lit tle window will appear. Press the desired key combination, and voilà, the correct mapping required by X will autofill for you. Associate your desired Action:, then press the Save & Apply & Exit button.

Any keyboard mappings you create using this utility will be saved to a file called ~/.xbindkeysrc.

See Also

  • man kbdcontrol
  • man atkbd 
  • The xbindkeys web site (http://hocwp.free.fr/xbindkeys/ xbindkeys.html)

Please check back next week for the continuation of this article.

Google+ Comments

Google+ Comments