#!/bin/bash set +e # VT100 colors declare -r BLACK=$'\e[1;30m' declare -r DK_GRAY=$'\e[0;30m' declare -r RED=$'\e[31m' declare -r GREEN=$'\e[32m' declare -r YELLOW=$'\e[33m' declare -r BLUE=$'\e[34m' declare -r MAGENTA=$'\e[35m' declare -r CYAN=$'\e[36m' declare -r WHITE=$'\e[37m' declare -r OFF=$'\e[0m' declare -r BOLD=$'\e[1m' declare -r REVERSE=$'\e[7m' declare -r HIDDEN=$'\e[8m' declare -r tab_=$'\t' declare -r nl_=$'\n' declare -r DD_BORDER="${BOLD}${WHITE}==============================================================================${OFF}" declare -r SD_BORDER="${BOLD}${WHITE}------------------------------------------------------------------------------${OFF}" declare -r STAR_BORDER="${BOLD}${WHITE}******************************************************************************${OFF}" # bold yellow > < pair declare -r R_arrow=$'\e[1;33m>\e[0m' declare -r L_arrow=$'\e[1;33m<\e[0m' usage() { 'clear' cat <<- -EOF- ${DD_BORDER} ${BOLD} Usage: $0 ${BOLD}[OPTION] Options: ${BOLD} -h, --help${OFF} print this help, then exit ${BOLD} --readme${OFF} print a small readme file, then exit ${BOLD} -V, --version${OFF} print version number, then exit ${BOLD} -d --directory DIR${OFF} use DIR directory for building HLFS; all files jhahlfs produces will be in the directory DIR/jhahlfs. Default is \"/mnt/lfs\". ${BOLD} --rebuild${OFF} clean the build directory before to perfom any other task. The directory is cleaned only if it was populated by a previous jhahlfs run. ${BOLD} -P, --get-packages${OFF} download the packages and patches. This assumes that the server declared in the jhahlfs.conf file has the proper packages and patches for the book version being processed. ${BOLD} -D, --download-client CLIENT use CLIENT as the program for retrieving packages (use in conjunction with -P) ${BOLD} -W, --working-copy DIR${OFF} use the local working copy placed in DIR as the HLFS book ${BOLD} -L, --HLFS-version VER${OFF} checkout VER version of the HLFS book. Supported versions at this time are: dev* | trunk | SVN aliases for Development HLFS ${BOLD} --fstab FILE${OFF} use FILE as the /etc/fstab file for the HLFS system. If not specified, a default /etc/fstab file with dummy values is created. ${BOLD} -C, --kernel-config FILE${OFF} use the kernel configuration file specified in FILE to build the kernel. if the file is not found, or if not specified, the kernel build is skipped. ${BOLD} -M, --run-make${OFF} run make on the generated Makefile ${DD_BORDER} -EOF- exit } blfs_usage() { 'clear' cat <<- -EOF- ${DD_BORDER} ${BOLD} Usage: $0 ${BOLD}[OPTION] Options: ${BOLD} -h, --help${OFF} print this help, then exit ${BOLD} -V, --version${OFF} print version number, then exit ${BOLD} -B, --BLFS-version VER${OFF} checkout VER version of the BLFS book. If not set, the development version is used. Supported versions at this time are: dev* | trunk | SVN aliases for Development BLFS ${BOLD} -W, --working-copy DIR${OFF} use the local working copy placed in DIR as the BLFS book ${BOLD} -D, --dependencies TYPE${OFF} add dependencies of type TYPE to the build tree. If not set, both required a recommended are used. Possible values are: required only required dependecies are used recommended both required a recommended dependencies are used optional all dependencies are used ${BOLD} -S, --server SERVER${OFF} set the FTP/HTTP server used as fallback to download the packages. If not specified, the one set in jhablfs.conf is used. ${BOLD} -T, --testsuites${OFF} add support to run the optional testsuites ${DD_BORDER} -EOF- exit } _inline_doc=" This script, ${PROGNAME}, strives to create an accurate makefile directly from the xml files used to generate the Hardened Linux From Scratch document. The usage of this script assumes you have read and are familiar with the book and therefore the configuration variables found in jhahlfs.conf will have meaning to you. There are a limited number of command line switches which, if used, will override the config file settings. NOTES:: *. The resulting Makefile takes considerable time to run to completion, lay in a supply of caffeine beverages. *. It is recommended that you temporarily unpack your linux kernel and run and configure the kernal as per the book and save the resulting .config file. *. Chapter07 contains numerous command files which require customizing before you start console, profile, hosts, network, fstab, kernel. " version=" ${BOLD}\"${PROGNAME}\"${OFF} script module (development) \$Date$ Written by Jeremy Huntwork, Manuel Caneles Esparcia, George Boudreau This program is published under the ${BOLD}Gnu General Public License, Version 2.${OFF} " no_empty_builddir() { 'clear' cat <<- -EOF- ${DD_BORDER} ${tab_}${tab_}${BOLD}${RED}W A R N I N G${OFF} Looks like the \$BUILDDIR directory contains subdirectories from a previous HLFS build. Please format the partition mounted on \$BUILDDIR or set a different build directory before running jhahlfs. ${OFF} ${DD_BORDER} -EOF- exit } help="${nl_}Try '$0 --help' for more information." exit_missing_arg="\ echo \"Option '\$1' requires an argument\" >&2 echo \"\$help\" >&2 exit 1" no_dl_client="\ echo \"Could not find a way to download the CLFS sources.\" >&2 echo \"Attempting to continue.\" >&2" HEADER="# This file is automatically generated by jhalfs # DO NOT EDIT THIS FILE MANUALLY # # Generated on `date \"+%F %X %Z\"`" #----------------------------------# wrt_target() { # #----------------------------------# local i=$1 local PREV=$2 ( cat << EOF $i: $PREV @\$(call echo_message, Building) EOF ) >> $MKFILE.tmp } #----------------------------------# wrt_unpack() { # #----------------------------------# local FILE=$1 ( cat << EOF @\$(call unpack,$FILE) @ROOT=\`head -n1 /tmp/unpacked | sed 's@^./@@;s@/.*@@'\` && \\ echo "export PKGDIR=\$(MOUNT_PT)\$(SRC)/\$\$ROOT" > envars && \\ chown -R lfs \$(MOUNT_PT)\$(SRC)/\$\$ROOT EOF ) >> $MKFILE.tmp } #=============================# wrt_unpack3() { # Unpack and set 'ROOT' var #=============================# local FILE=$1 ( cat << EOF @\$(call unpack3,$FILE) @ROOT=\`head -n1 /tmp/unpacked | sed 's@^./@@;s@/.*@@'\` && \\ echo "export PKGDIR=\$(MOUNT_PT)\$(SRC)/\$\$ROOT" > envars && \\ chown -R lfs \$(MOUNT_PT)\$(SRC)/\$\$ROOT EOF ) >> $MKFILE.tmp } #----------------------------------# wrt_unpack2() { # #----------------------------------# local FILE=$1 ( cat << EOF @\$(call unpack2,$FILE) @ROOT=\`head -n1 /tmp/unpacked | sed 's@^./@@;s@/.*@@'\` && \\ echo "export PKGDIR=\$(SRC)/\$\$ROOT" > envars EOF ) >> $MKFILE.tmp } #=============================# wrt_unpack4() { # Unpack and set 'ROOT' var #=============================# local FILE=$1 ( cat << EOF @\$(call unpack4,$FILE) @ROOT=\`head -n1 /tmp/unpacked | sed 's@^./@@;s@/.*@@'\` && \\ echo "export PKGDIR=\$(SRC)/\$\$ROOT" > envars EOF ) >> $MKFILE.tmp } #----------------------------------# wrt_target_vars() { # Target vars for hlfs (cross-build method) #----------------------------------# ( cat << EOF echo "export target=$(uname -m)-${TARGET}" >> envars && \\ echo "export ldso=/lib/${LOADER}" >> envars EOF ) >> $MKFILE.tmp } #----------------------------------# wrt_run_as_su() { # #----------------------------------# local this_script=$1 local file=$2 ( cat << EOF @echo -e "\n\`date\`\n\nKB: \`du -sk --exclude={0,1}??-* \$(MOUNT_PT)\`\n" >logs/$this_script && \\ su - lfs -c "source /home/lfs/.bashrc && $JHALFSDIR/${PROGNAME}-commands/$file" >>logs/$this_script 2>&1 && \\ echo -e "\n\`date\`\n\nKB: \`du -sk --exclude={0,1}??-* \$(MOUNT_PT)\`\n" >>logs/$this_script EOF ) >> $MKFILE.tmp } #==================================# wrt_run_as_lfs() { # header to log file, execute script, footer to log file #==================================# local this_script=$1 local file=$2 ( cat << EOF @echo -e "\n\`date\`\n\nKB: \`du -sk --exclude={0,1}??-* \$(MOUNT_PT)\`\n" >logs/$this_script && \\ su - lfs -c "source /home/lfs/.bashrc && $JHALFSDIR/${PROGNAME}-commands/$file" >>logs/$this_script 2>&1 && \\ echo -e "\n\`date\`\n\nKB: \`du -sk --exclude={0,1}??-* \$(MOUNT_PT)\`\n" >>logs/$this_script EOF ) >> $MKFILE.tmp } #----------------------------------# wrt_run_as_root() { # #----------------------------------# local this_script=$1 local file=$2 ( cat << EOF @echo -e "\n\`date\`\n\nKB: \`du -sk --exclude={0,1}??-* \$(MOUNT_PT)\`\n" >logs/$this_script && \\ export LFS=\$(MOUNT_PT) && ${PROGNAME}-commands/$file >>logs/$this_script 2>&1 && \\ echo -e "\n\`date\`\n\nKB: \`du -sk --exclude={0,1}??-* \$(MOUNT_PT)\`\n" >>logs/$this_script EOF ) >> $MKFILE.tmp } #=============================# wrt_run_as_root2() { # Some scripts must be run as root.. #=============================# local this_script=$1 local file=$2 ( cat << EOF @echo -e "\n\`date\`\n\nKB: \`du -sk --exclude={0,1}??-* \`\n" >logs/$this_script && \\ source envars && ${PROGNAME}-commands/$file >>logs/$this_script 2>&1 && \\ echo -e "\n\`date\`\n\nKB: \`du -sk --exclude={0,1}??-* \`\n" >>logs/$this_script EOF ) >> $MKFILE.tmp } #----------------------------------# wrt_remove_build_dirs() { # #----------------------------------# local name=$1 ( cat << EOF @ROOT=\`head -n1 /tmp/unpacked | sed 's@^./@@;s@/.*@@'\` && \\ rm -r \$(MOUNT_PT)\$(SRC)/\$\$ROOT && \\ if [ -e \$(MOUNT_PT)\$(SRC)/$name-build ]; then \\ rm -r \$(MOUNT_PT)\$(SRC)/$name-build; \\ fi; EOF ) >> $MKFILE.tmp } #----------------------------------# wrt_remove_build_dirs2() { # #----------------------------------# local name=$1 ( cat << EOF @ROOT=\`head -n1 /tmp/unpacked | sed 's@^./@@;s@/.*@@'\` && \\ rm -r \$(SRC)/\$\$ROOT && \\ if [ -e \$(SRC)/$name-build ]; then \\ rm -r \$(SRC)/$name-build; \\ fi; EOF ) >> $MKFILE.tmp } #----------------------------------# wrt_run_as_chroot1() { # #----------------------------------# local this_script=$1 local file=$2 ( cat << EOF @echo -e "\n\`date\`\n\nKB: \`du -skx --exclude={0,1}??-* \$(MOUNT_PT)\`\n" >logs/${this_script} && \\ \$(CHROOT1) 'cd /jhalfs && source envars && /jhalfs/${PROGNAME}-commands/$file >>/jhalfs/logs/${this_script} 2>&1' && \\ echo -e "\n\`date\`\n\nKB: \`du -skx --exclude={0,1}??-* \$(MOUNT_PT)\`\n" >>logs/${this_script} EOF ) >> $MKFILE.tmp } #----------------------------------# wrt_run_as_chroot2() { # #----------------------------------# local this_script=$1 local file=$2 ( cat << EOF @echo -e "\n\`date\`\n\nKB: \`du -skx --exclude={0,1}??-* \$(MOUNT_PT)\`\n" >logs/${this_script} && \\ \$(CHROOT2) 'cd /jhalfs && source envars && /jhalfs/${PROGNAME}-commands/$file >>/jhalfs/logs/${this_script} 2>&1' && \\ echo -e "\n\`date\`\n\nKB: \`du -skx --exclude={0,1}??-* \$(MOUNT_PT)\`\n" >>logs/${this_script} EOF ) >> $MKFILE.tmp } #----------------------------------# wrt_copy_fstab() { # #----------------------------------# local i=$1 ( cat << EOF @echo -e "\n\`date\`\n\nKB: \`du -skx --exclude={0,1}??-* \$(MOUNT_PT)\`\n" >logs/$i && \\ cp -v $FSTAB \$(MOUNT_PT)/etc/fstab >>logs/$i 2>&1 && \\ echo -e "\n\`date\`\n\nKB: \`du -skx --exclude={0,1}??-* \$(MOUNT_PT)\`\n" >>logs/$i EOF ) >> $MKFILE.tmp } #----------------------------------# wrt_copy_fstab2() { # #----------------------------------# local i=$1 ( cat << EOF @echo -e "\n\`date\`\n\nKB: \`du -skx --exclude={0,1}??-* \`\n" >logs/$i && \\ cp -v $FSTAB /etc/fstab >>logs/$i 2>&1 && \\ echo -e "\n\`date\`\n\nKB: \`du -skx --exclude={0,1}??-* \`\n" >>logs/$i EOF ) >> $MKFILE.tmp } #----------------------------------# wrt_export_timezone() { # #----------------------------------# echo -e '\t@echo "export TIMEZONE=$(TIMEZONE)" >> envars' >> $MKFILE.tmp } #----------------------------------# wrt_export_pagesize() { # #----------------------------------# echo -e '\t@echo "export PAGE=$(PAGE)" >> envars' >> $MKFILE.tmp } #----------------------------------# wrt_export_pkgdir() { # #----------------------------------# ( cat << EOF @echo "export PKGDIR=\$(SRC)/binutils-build" > envars EOF ) >> $MKFILE.tmp } #----------------------------# run_make() { #----------------------------# # Test if make must be run. if [ "$RUNMAKE" = "1" ] ; then # Test to make sure we're running the build as root if [ "$UID" != "0" ] ; then echo "You must be logged in as root to successfully build LFS." exit 1 fi # Build the system if [ -e $MKFILE ] ; then echo -ne "Building the LFS system...\n" cd $JHALFSDIR && make -f ${PROGNAME}-Makefile echo -ne "done\n" fi fi } #----------------------------# clean_builddir() { #----------------------------# # Test if the clean must be done. if [ "$CLEAN" = "1" ] ; then # Test to make sure we're running the clean as root if [ "$UID" != "0" ] ; then echo "You must be logged in as root to clean the build directory." exit 1 fi # Test to make sure that the build directory was populated by jhalfs if [ ! -d $JHALFSDIR ] || [ ! -d $BUILDDIR/sources ] ; then echo "Looks like $BUILDDIR was not populated by a previous jhalfs run." exit 1 else # Clean the build directory echo -ne "Cleaning $BUILDDIR...\n" rm -rf $BUILDDIR/{bin,boot,dev,etc,home,lib,media,mnt,opt,proc,root,sbin,srv,sys,tmp,tools,usr,var} echo -ne "Cleaning $JHALFSDIR...\n" rm -rf $JHALFSDIR/{0*,1*,envars,sources-dir,commands,logs,Makefile,dump-lfs-scripts.xsl,functions,packages,patches} echo -ne "Cleaning remainig extracted sources in $BUILDDIR/sources...\n" rm -rf `find $BUILDDIR/sources/* -maxdepth 0 -type d` echo -ne "done\n" fi fi } #----------------------------# get_book() { #----------------------------# cd $JHALFSDIR if [ -z $WC ] ; then # Check for Subversion instead of just letting the script hit 'svn' and fail. test `type -p svn` || eval "echo \"This feature requires Subversion.\" exit 1" echo -n "Downloading the $PROGNAME document, $LFSVRS version... " case $PROGNAME in lfs) svn_root="LFS" ;; hlfs) svn_root="HLFS" ;; clfs) svn_root="cross-lfs" ;; blfs) svn_root="BLFS" ;; *) echo "BOOK not defined in function " exit 1 ;; esac # Grab a fresh LFS book if it's missing, otherwise, update it from the # repo. If we've already extracted the commands, move on to getting the # sources. if [ -d ${PROGNAME}-$LFSVRS ] ; then cd ${PROGNAME}-$LFSVRS if LC_ALL=C svn up | grep -q At && test -d $JHALFSDIR/commands && \ test -f $JHALFSDIR/packages && test -f $JHALFSDIR/patches ; then echo -ne "done\n" # Set the canonical book version cd $JHALFSDIR VERSION=`grep "ENTITY version " $BOOK/general.ent | sed 's@@@'` get_sources else echo -ne "done\n" # Set the canonical book version cd $JHALFSDIR VERSION=`grep "ENTITY version " $BOOK/general.ent | sed 's@@@'` extract_commands fi else case $LFSVRS in development) svn co $SVN/${svn_root}/trunk/BOOK ${PROGNAME}-$LFSVRS >>$LOGDIR/$LOG 2>&1 ;; alphabetical) svn co $SVN/${svn_root}/branches/$LFSVRS/BOOK ${PROGNAME}-$LFSVRS >>$LOGDIR/$LOG 2>&1 ;; esac echo -ne "done\n" # Set the canonical book version cd $JHALFSDIR VERSION=`grep "ENTITY version " $BOOK/general.ent | sed 's@@@'` extract_commands fi else echo -ne "Using $BOOK as book's sources ...\n" # Set the canonical book version cd $JHALFSDIR VERSION=`grep "ENTITY version " $BOOK/general.ent | sed 's@@@'` extract_commands fi } #----------------------------# build_patches_file() { # Supply a suitably formated list of patches. #----------------------------# local IFS echo -ne "Creating the patch file list " LOC_add_patches_entry() { for f in `grep "/$1-" patcheslist_.wget`; do basename $f | sed "s|${2}|\&${1}-version;|" >> patches done } xsltproc --nonet \ --xinclude \ -o patcheslist_.wget \ patcheslist.xsl \ $BOOK/index.xml #> /dev/null 2>&1 rm -f patches IFS=$'\x0A' # Modify the 'internal field separator' to break on 'LF' only for f in `cat packages`; do LOC_add_patches_entry \ `echo $f | sed -e 's/-version//' \ -e 's/-file.*//' \ -e 's/"//g' \ -e 's/uclibc/uClibc/'` done # .... U G L Y .... what to do with the grsecurity patch to the kernel.. for f in `grep "/grsecurity-" patcheslist_.wget`; do basename $f >> patches done rm -f patcheslist_.wget echo "...OK" } #----------------------------# extract_commands() { # #----------------------------# # Check for libxslt instead of just letting the script hit 'xsltproc' and fail. test `type -p xsltproc` || eval "echo \"This feature requires libxslt.\" exit 1" cd $JHALFSDIR VERSION=`grep "ENTITY version " $BOOK/general.ent | sed 's@@@'` # Start clean if [ -d commands ]; then rm -rf commands mkdir -v commands fi echo -n "Extracting commands for" # Dump the commands in shell script form from the HLFS book. case ${PROGNAME} in clfs) echo -n "${tab_} ${L_arrow}${BOLD}$ARCH${R_arrow} target architecture" xsltproc --xinclude \ --nonet \ --output ./${PROGNAME}-commands/ \ $BOOK/stylesheets/dump-commands.xsl $BOOK/$ARCH-index.xml ;; hlfs) echo -n "${tab_} ${L_arrow}${BOLD}$MODEL${R_arrow} HLFS architecture" xsltproc --nonet \ --xinclude \ --stringparam model $MODEL \ --stringparam testsuite $TEST \ --stringparam testchaintest 0 \ --stringparam vim-lang $VIMLANG \ -o ./${PROGNAME}-commands/ $XSL $BOOK/index.xml >>$LOGDIR/$LOG 2>&1 ;; lfs) echo -n "${tab_} ${L_arrow}${BOLD}LFS${R_arrow} build" xsltproc --nonet \ --xinclude \ --stringparam testsuite $TEST \ --stringparam vim-lang $VIMLANG \ -o ./${PROGNAME}-commands/ $XSL $BOOK/index.xml >>$LOGDIR/$LOG 2>&1 ;; blfs) echo -n "${tab_} ${L_arrow}${BOLD}BLFS${R_arrow} build" xsltproc --nonet \ --xinclude \ --stringparam testsuite $TEST \ --stringparam server $SERVER \ -o ./${PROGNAME}-commands/ $XSL $BOOK/index.xml >>$LOGDIR/$LOG 2>&1 ;; *) exit 1 esac echo " ...OK" # Make the scripts executable. chmod -R +x $JHALFSDIR/${PROGNAME}-commands # Grab the patches and package names. cd $JHALFSDIR for i in patches packages ; do rm -f $i ; done grep "\-version" $BOOK/general.ent | sed -e 's@@"@' \ -e '/generic/d' >> packages # Download the vim-lang package if it must be installed if [ "$VIMLANG" = "1" ] ; then echo `grep "vim" packages | sed 's@vim@&-lang@'` >> packages fi echo `grep "udev-config-file" $BOOK/general.ent | sed -e 's@@"@'` >> packages # There is no HLFS patches.ent file so we will create one. case "${PROGNAME}" in hlfs) build_patches_file ;; clfs) ;; lfs) ;; blfs) ;; *) exit 1 esac # Done. Moving on... get_sources } #----------------------------# download() { # Download file, write name to MISSING_FILES.DMP if an error #----------------------------# cd $BUILDDIR/sources # Hackish fix for the bash-doc, glibc-{linuxthreads,libidn} # that don't conform to norms in the URL scheme. DIR=`echo $1 | sed 's@-doc@@;s@-linuxthreads@@;s@-libidn@@;s@-testsuite@@'` # Find the md5 sum for this package. if [ $2 != MD5SUMS ] ; then set +e MD5=`grep " $2" MD5SUMS` if [ $? -ne 0 ]; then set -e echo "${RED}$2 not found in MD5SUMS${OFF}" echo "$2 not found in MD5SUMS" >> MISSING_FILES.DMP return fi set -e fi if [ ! -f $2 ] ; then case $DL in wget ) wget --passive $FTP/$DIR/$2 ;; curl ) `curl -# $FTP/$DIR/$2 -o $2` ;; * ) echo "$DL not supported at this time." ;; esac elif ! echo "$MD5" | md5sum -c - >/dev/null 2>/dev/null ; then case $DL in wget ) wget --passive -c $FTP/$DIR/$2 ;; curl ) `curl -# -C - $FTP/$DIR/$2 -o $2` ;; * ) echo "$DL not supported at this time." ;; esac fi if [ $2 != MD5SUMS ] && ! echo "$MD5" | md5sum -c - ; then exit 1 fi if [ $2 != MD5SUMS ] ; then echo `grep "$MD5" MD5SUMS` >> MD5SUMS-$VERSION fi } #----------------------------# get_sources() { #----------------------------# # Test if the packages must be downloaded if [ "$HPKG" = "1" ] ; then # This variable is necessary to make sure the `cat $JHALFSDIR/packages` # separates each iteration by lines. It is necessary to have the second # ' on the next line. IFS=' ' if [ ! -d $BUILDDIR/sources ] ; then mkdir $BUILDDIR/sources ; fi cd $BUILDDIR/sources if [ -f MD5SUMS ] ; then rm MD5SUMS ; fi if [ -f MD5SUMS-$VERSION ] ; then rm MD5SUMS-$VERSION ; fi download "" MD5SUMS # Iterate through each package and grab it, along with any patches it needs. for i in `cat $JHALFSDIR/packages` ; do PKG=`echo $i | sed -e 's/-version.*//' -e 's/-file.*//'` # There are some entities that aren't valid packages. if [ "$PKG" = "expect-lib" -o "$PKG" = "linux-dl" ] ; then continue ; fi VRS=`echo $i | sed -e 's/.* //' -e 's/"//g'` case $PKG in tcl) FILE="$PKG$VRS-src.tar.bz2" ;; vim-lang) PKG="vim" FILE="vim-$VRS-lang.tar.bz2" ;; udev-config) PKG="udev" FILE="$VRS" ;; *) FILE="$PKG-$VRS.tar.bz2" ;; esac download $PKG $FILE # Download any associated patches for patch in `grep "&$PKG-version" $JHALFSDIR/patches` ; do PATCH=`echo $patch | sed 's@&'$PKG'-version;@'$VRS'@'` download $PKG $PATCH done done fi } #-----------------------------------------------# _IS_() # Function to test build scripts names #-----------------------------------------------# { # Returns substr $2 or null str # Must use string testing case $1 in *$2*) echo "$2" ;; *) echo "" ;; esac }