Files
odroid-stamper/functions
Dongjin Kim 13ea129d26 function: rename 'distro/distro_version()' to 'osname/osversion()'
Signed-off-by: Dongjin Kim <tobetter@gmail.com>
Change-Id: I4f0a6cd2768ed5c600ca3901dfbc17b8e7e243ed
2022-06-01 01:42:41 +09:00

661 lines
19 KiB
Bash

boot_mnt=${build_dir}/boot
rootfs_mnt=${build_dir}/rootfs
fixups_dir=/host/fixups
do_unmount() {
echo "I: unmounting devices..."
if [ ! -b ${TARGET_DEVICE} ]; then
if [ "x${LOOPBACK}" != "x" ]; then
losetup -d ${LOOPBACK} 2>/dev/null
fi
fi
if [ -f ${TOPDIR}/custom/${CUSTOMOS}/hooks/umount ]; then
. ${TOPDIR}/custom/${CUSTOMOS}/hooks/umount
fi
umount -f ${rootfs_mnt}/sys 2>/dev/null
umount -f ${rootfs_mnt}/proc 2>/dev/null
umount -f ${rootfs_mnt}/dev/pts 2>/dev/null
umount -f ${rootfs_mnt}/dev 2>/dev/null
umount -f ${rootfs_mnt}/boot 2>/dev/null
}
cleanup() {
do_unmount
if [ -f ${TOPDIR}/custom/${CUSTOMOS}/hooks/umount ]; then
. ${TOPDIR}/custom/${CUSTOMOS}/hooks/umount
fi
umount -f ${boot_mnt} ${rootfs_mnt} 2>/dev/null
if [ "x${opt_keep_builddir}" != "xtrue" ]; then
rm -rf ${boot_mnt} ${rootfs_mnt}
fi
if [ "$(ls ${build_dir} | wc -l)" = "0" ]; then
rm -rf ${build_dir}
fi
}
trap_ctrlc() {
echo "<Ctrl-C> is detected..."
cleanup
exit 1
}
panic() {
echo "E: ${1}"
cleanup
exit 1
}
fixup_arch() {
case "${1}" in
arm64)
echo "aarch64"
;;
armhf)
echo "arm"
;;
*)
panic "Unknown architecture..."
;;
esac
}
qemu_binary() {
if [ "x$(fixup_arch ${1})" != "x$(uname -m)" ]; then
echo "/usr/bin/qemu-$(fixup_arch ${ARCH})-static"
fi
}
ask_password() {
printf '%s\n' "$(dialog --output-fd 1 \
--passwordbox "Enter your sudo password" 12 50)" | sudo -Svp ''
}
get_part_boot() {
if [ ! -b ${TARGET_DEVICE} ]; then
echo "${LOOPBACK}p1"
else
echo "${TARGET_DEVICE}-part1"
fi
}
get_part_root() {
if [ ! -b ${TARGET_DEVICE} ]; then
echo "${LOOPBACK}p2"
else
echo "${TARGET_DEVICE}-part2"
fi
}
get_part_ext() {
if [ ! -b ${TARGET_DEVICE} ]; then
echo "${LOOPBACK}p${1}"
else
echo "${TARGET_DEVICE}-part${1}"
fi
}
get_kernel_cmdline_defaults() {
echo ""
}
get_default_apt_options() {
echo "-y --no-install-recommends"
}
get_packages() {
[ -f ${1} ] && cat ${1} | awk -F '#|;' '{print $1}'
}
get_flavour_packages() {
local pkgs=""
pkgs="${pkgs} $(get_packages distro/${DISTRO}/common/packages)"
pkgs="${pkgs} $(get_packages distro/${DISTRO}/${FLAVOUR}/packages)"
pkgs="${pkgs} $(get_packages boards/${BOARD}/packages)"
pkgs="${pkgs} $(get_packages boards/${BOARD}/distro/${DISTRO}/${FLAVOUR}/packages)"
[ -f custom/${CUSTOMOS}/packages ] &&
pkgs="${pkgs} $(get_packages custom/${CUSTOMOS}/packages)"
if [ "x${LIVESYSTEM}" = "xtrue" ]; then
pkgs="${pkgs} casper"
fi
echo ${pkgs} | tr " " "\n" | sort | uniq | tr "\n" " "
}
get_blacklist_packages() {
local pkgs=""
pkgs="${pkgs} $(get_packages distro/${DISTRO}/common/blacklist)"
pkgs="${pkgs} $(get_packages distro/${DISTRO}/${FLAVOUR}/blacklist)"
pkgs="${pkgs} $(get_packages boards/${BOARD}/blacklist)"
pkgs="${pkgs} $(get_packages boards/${BOARD}/distro/${DISTRO}/${FLAVOUR}/blacklist)"
[ -f custom/${CUSTOMOS}/blacklist ] &&
pkgs="${pkgs} $(get_packages custom/${CUSTOMOS}/blacklist)"
echo ${pkgs} | tr " " "\n" | sort | uniq | tr "\n" " "
}
get_lookback_device() {
echo `losetup -a | grep ${1} | cut -d':' -f1`
}
oem_mount() {
local src=${TOPDIR}/custom/${CUSTOMOS}/part${1}
local dst=${rootfs_mnt}/${2}
mkdir -p ${src}
mkdir -p ${dst}
mount -o bind ${src} ${dst}
}
oem_umount() {
umount -f ${rootfs_mnt}/${1} 2>/dev/null
}
oem_mkfs_ext4() {
local src=${3}
local part=$(get_part_ext ${1})
local label=""
[ "x${2}" = "x" ] || label="-L ${2}"
# Build image with the file list
if [ -f ${TOPDIR}/custom/${CUSTOMOS}/${src} ]; then
local overlayfs_dir=${build_dir}/overlayfs
rm -rf ${overlayfs_dir} && mkdir -p ${overlayfs_dir}
while read line; do
file=$(echo "${line}" | awk -F '#|;' '{print $1}')
if [ ! -z "${file}" ]; then
if [ -d "${file}" ]; then
dir=$(dirname ${file})
mkdir -p ${overlayfs_dir}/${dir}
mv ${rootfs_mnt}/${file} ${overlayfs_dir}/${dir}/
else
for f in $(ls ${file} 1>/dev/null 2>&1); do
dir=$(dirname ${f})
mkdir -p ${overlayfs_dir}/${dir}
mv ${rootfs_mnt}/${f} ${overlayfs_dir}/${dir}/
done
fi
fi
done < ${TOPDIR}/custom/${CUSTOMOS}/${src}
# Copy file list to root of partition
cp -a ${TOPDIR}/custom/${CUSTOMOS}/${src} \
${overlayfs_dir}/.filesystem
src=${overlayfs_dir}
else
if [ "x${src}" = "x" ]; then
src=${TOPDIR}/custom/${CUSTOMOS}/part${1}
fi
fi
if [ -d ${src} ]; then
echo "* Creating ext4 file system to ${part} with ${src}"
sudo mkfs.ext4 -F ${label} -d ${src} ${part} 2>/dev/null \
|| panic "failed to create partition '${part}'"
fi
}
do_mount() {
echo "I: mounting to ${rootfs_mnt}"
mkdir -p ${rootfs_mnt}/boot
mkdir -p ${rootfs_mnt}/dev
mkdir -p ${rootfs_mnt}/dev/pts
mkdir -p ${rootfs_mnt}/proc
mkdir -p ${rootfs_mnt}/sys
mount -o bind ${boot_mnt} ${rootfs_mnt}/boot
mount -o bind /dev ${rootfs_mnt}/dev
mount -o bind /dev/pts ${rootfs_mnt}/dev/pts
mount -o bind /proc ${rootfs_mnt}/proc
mount -o bind /sys ${rootfs_mnt}/sys
if [ -f ${TOPDIR}/custom/${CUSTOMOS}/hooks/mount ]; then
. ${TOPDIR}/custom/${CUSTOMOS}/hooks/mount
fi
}
get_uuid_by_path() {
grep " ${1} " ${rootfs_mnt}/etc/fstab | cut -d' ' -f1 | cut -d'=' -f2 | tr -d '"'
}
do_create_squashfs() {
local live_image=filesystem.squashfs
local disk=${1}
local boot_pstart=$(get_reserved_sectors)
[ "x${boot_pstart}" = "x" ] && boot_pstart=2048
sudo rm -f ${live_image}
sudo mksquashfs ${rootfs_mnt} ${live_image} -comp lzo || exit 1
mkdir -p ${boot_mnt}/casper
cp ${live_image} ${boot_mnt}/casper/ && rm -f ${live_image}
local boot_blk=$(($(du -s --block-size=512 ${boot_mnt} | cut -f1) * 12 / 10 - 1))
local blocks=$((${boot_pstart} + ${boot_blk}))
if [ -f ${TOPDIR}/custom/${CUSTOMOS}/partitions ]; then
. ${TOPDIR}/custom/${CUSTOMOS}/partitions
blocks=${TOTAL_SECTORS}
fi
echo "I: creating the disk image file '${disk}' ($((${blocks} * 512))KiB)"
dd if=/dev/zero bs=512 count=${blocks} \
| pv -s $((${blocks} * 512 * 1024))k \
| dd of=${disk} >/dev/null
[ "${BASH_VERSION}" = "" ] || BACKSLASH_ESCAPE="-e"
echo "I: creating a partition table to '${disk}'"
if [ -f ${TOPDIR}/custom/${CUSTOMOS}/partitions ]; then
echo ${BACKSLASH_ESCAPE} \
$(${TOPDIR}/fdiskcmd.sh ${TOPDIR}/custom/${CUSTOMOS}/partitions) \
| fdisk ${disk} >/dev/null
fi
echo ${BACKSLASH_ESCAPE} \
"n\np\n\n${boot_pstart}\n\n" \
"w\n" | fdisk ${disk} >/dev/null
partprobe
if [ ! -b ${disk} ]; then
losetup -fP ${disk} || panic "failed to setup loopback image"
LOOPBACK=$(get_lookback_device ${disk})
echo ${LOOPBACK}
fi
if [ -f ${TOPDIR}/custom/${CUSTOMOS}/hooks/partition ]; then
. ${TOPDIR}/custom/${CUSTOMOS}/hooks/partition
fi
UUID_BOOT=$(get_uuid_by_path "/boot")
sudo mkfs.ext2 -F -L BOOT -U ${UUID_BOOT} \
-d ${boot_mnt} $(get_part_boot) 2>/dev/null \
|| panic "failed to create partition '$(get_part_boot)'"
}
do_create_partition() {
local disk=${1}
local boot_blk=$((${2} * 1024 * 1024 / 512 - 1))
local root_blk=$(($(du -s --block-size=512 ${rootfs_mnt} | cut -f1) * 12 / 10))
local boot_pstart=$(get_reserved_sectors)
[ "x${boot_pstart}" = "x" ] && boot_pstart=2048
local boot_size=$((${boot_pstart} + ${boot_blk}))
local blocks=$((${boot_pstart} + ${boot_blk} + ${root_blk}))
if [ -f ${TOPDIR}/custom/${CUSTOMOS}/partitions ]; then
. ${TOPDIR}/custom/${CUSTOMOS}/partitions
blocks=${TOTAL_SECTORS}
fi
if [ ! -b ${disk} ]; then
echo "I: creating the disk image file '${disk}' ($((${blocks} * 512))KiB)"
dd if=/dev/zero bs=512 count=${blocks} \
| pv -s $((${blocks} * 512 * 1024))k \
| dd of=${disk} >/dev/null
else
for d in `readlink -f ${disk}*`; do
umount -f ${d} 2>/dev/null
done
echo "I: removing the current partition table in '${disk}'"
dd if=/dev/zero of=${disk} bs=512 count=1 >/dev/null
fi
[ "${BASH_VERSION}" = "" ] || BACKSLASH_ESCAPE="-e"
echo "I: creating a partition table to '${disk}'"
if [ -f ${TOPDIR}/custom/${CUSTOMOS}/partitions ]; then
echo ${BACKSLASH_ESCAPE} \
$(${TOPDIR}/fdiskcmd.sh ${TOPDIR}/custom/${CUSTOMOS}/partitions) \
| fdisk ${disk} >/dev/null
fi
echo ${BACKSLASH_ESCAPE} \
"n\np\n\n${boot_pstart}\n${boot_size}\n" \
"n\np\n\n$((${boot_size} + 1))\n\n" \
"w\n" | fdisk ${disk} >/dev/null
partprobe
if [ ! -b ${disk} ]; then
losetup -fP ${disk} || panic "failed to setup loopback image"
LOOPBACK=$(get_lookback_device ${disk})
echo ${LOOPBACK}
fi
if [ -f ${TOPDIR}/custom/${CUSTOMOS}/hooks/partition ]; then
. ${TOPDIR}/custom/${CUSTOMOS}/hooks/partition
fi
UUID_BOOT=$(get_uuid_by_path "/boot")
UUID_ROOT=$(get_uuid_by_path "/")
sudo mkfs.ext2 -F -L BOOT -U ${UUID_BOOT} \
-d ${boot_mnt} $(get_part_boot) 2>/dev/null \
|| panic "failed to create partition '$(get_part_boot)'"
sudo mkfs.ext4 -F -L rootfs -U ${UUID_ROOT} \
-d ${rootfs_mnt} $(get_part_root) 2>/dev/null \
|| panic "failed to create partition '$(get_part_root)'"
}
do_extract() {
local file=${1}
[ -f ${file} ] || panic "missing file ${file}"
echo "I: extracting ${file} to ${rootfs_mnt}..."
case ${file} in
*.squashfs)
sudo unsquashfs -f -d ${rootfs_mnt} ${file}
;;
*.tar.gz)
pv ${file} \
| tar xzf - --strip-component=1 -C ${rootfs_mnt} \
|| panic "E: error while extracting"
;;
esac
}
do_preinstall() {
local UUID_BOOT=$(uuidgen)
local UUID_ROOT=$(uuidgen)
echo "I: running preinstall scripts and copy default files..."
mkdir -p ${rootfs_mnt}${fixups_dir}
[ -d ${TOPDIR}/fixups ] && rsync -a ${TOPDIR}/fixups/* ${rootfs_mnt}/${fixups_dir}
[ -d ${WORKDIR}/fixups ] && rsync -a ${WORKDIR}/fixups/* ${rootfs_mnt}/${fixups_dir}
[ -d ${TOPDIR}/distro/${DISTRO}/${FLAVOUR}/fixups ] \
&& rsync -a ${TOPDIR}/distro/${DISTRO}/${FLAVOUR}/fixups/* ${rootfs_mnt}/${fixups_dir}
[ -d ${TOPDIR}/boards/${BOARD}/fixups ] \
&& rsync -a ${TOPDIR}/boards/${BOARD}/fixups/* ${rootfs_mnt}/${fixups_dir}
[ -d ${TOPDIR}/custom/${CUSTOMOS}/fixups ] \
&& rsync -a ${TOPDIR}/custom/${CUSTOMOS}/fixups/* ${rootfs_mnt}/${fixups_dir}
case ${FLAVOUR} in
*-desktop*)
faceimage=${TOPDIR}/face.png
if [ -f ${TOPDIR}/boards/${BOARD}/face.png ]; then
faceimage=${TOPDIR}/boards/${BOARD}/face.png
elif [ -f ${TOPDIR}/custom/${CUSTOMOS}/face.png ]; then
faceimage=${TOPDIR}/custom/${CUSTOMOS}/face.png
fi
cp -a ${faceimage} ${rootfs_mnt}/tmp
;;
esac
FIXUPS=`ls -A1 ${rootfs_mnt}${fixups_dir}/*`
for fixup in ${FIXUPS}; do
sed -i \
-e "s,@@DEFAULT_BOARD@@,${BOARD},g" \
-e "s,@@DEFAULT_HOSTNAME@@,${FLAVOUR},g" \
-e "s,@@DEFAULT_APT_OPTIONS@@,$(get_default_apt_options ${FLAVOUR}),g" \
-e "s,@@DEFAULT_FLAVOUR_PACKAGES@@,$(get_flavour_packages),g" \
-e "s,@@DEFAULT_BLACKLIST_PACKAGES@@,$(get_blacklist_packages),g" \
-e "s,@@DEFAULT_KERNEL_PACKAGE@@,$(get_kernel_package),g" \
-e "s,@@DEFAULT_BOOTSCRIPT_PACKAGE@@,$(get_bootscript_package),g" \
-e "s,@@DEFAULT_MACHINE_NAME@@,$(get_machine_name),g" \
-e "s,@@LINUX_KERNEL_CMDLINE@@,root=UUID=${UUID_ROOT} rootwait ro quiet,g" \
-e "s,@@LINUX_KERNEL_CMDLINE_DEFAULTS@@,$(get_kernel_cmdline_defaults),g" \
-e "s,@@DEFAULT_DEV_BOOT@@,${UUID_BOOT},g" \
-e "s,@@DEFAULT_DEV_ROOTFS@@,${UUID_ROOT},g" \
-e "s,@@ALLOW_ROOT_LOGIN@@,$(allow_root_login),g" \
-e "s,@@DEFAULT_USER@@,$(default_user),g" \
-e "s,@@DEFAULT_USER_PASSWD@@,$(default_user_passwd),g" \
-e "s,@@DEFAULT_DISTRO@@,${DISTRO},g" \
-e "s,@@OSNAME@@,$(osname ${DISTRO}),g" \
-e "s,@@LIVESYSTEM@@,${LIVESYSTEM},g" \
-e "s,@@MACHINEID@@,$(dbus-uuidgen),g" \
-e "s,@@INTERNAL@@,${INTERNAL},g" \
${fixup}
done
QEMU_BINARY=$(qemu_binary ${ARCH})
if [ ! "x${QEMU_BINARY}" = "x" ]; then
cp ${QEMU_BINARY} ${rootfs_mnt}/${QEMU_BINARY} || panic "error"
fi
}
do_postinstall() {
echo "I: removing preinstall scripts..."
if [ ! "x${QEMU_BINARY}" = "x" ]; then
rm -f ${rootfs_mnt}/${QEMU_BINARY}
fi
rm -f ${rootfs_mnt}/tmp/face.png
rm -rf ${rootfs_mnt}${fixups_dir}
rm -rf ${rootfs_mnt}/etc/apt/sources.list.d/ppa_linuxfactory_or_kr.list
}
do_cleanup() {
rm -f ${rootfs_mnt}/SHA256SUMS
rm -rf ${rootfs_mnt}/boot/filesystem.*
}
get_install_fixups() {
echo `ls -A1 ${rootfs_mnt}${fixups_dir}/S* | sort`
}
do_copy_overlays() {
[ -d ${1} ] && rsync -a ${1}/* ${rootfs_mnt}/overlay/
}
do_run_fixups() {
mkdir -p ${rootfs_mnt}/overlay
do_copy_overlays ${TOPDIR}/overlay
do_copy_overlays ${WORKDIR}/overlay
do_copy_overlays ${TOPDIR}/distro/${DISTRO}/common/overlay
do_copy_overlays ${TOPDIR}/distro/${DISTRO}/${FLAVOUR}/overlay
do_copy_overlays ${TOPDIR}/custom/${CUSTOMOS}/overlay
for fixup in $(get_install_fixups); do
chroot ${rootfs_mnt} ${QEMU_BINARY} \
/bin/sh ${fixups_dir}/$(basename ${fixup}) || \
panic "error when running the fixup"
done
}
do_query_latest_tarball() {
local host_url=${1}
local arch=${2}
local codename=${3}
local flavour=${4}
case ${flavour} in
*-desktop* | weston)
flavour="server"
;;
esac
file=$(lynx -dump -listonly ${host_url}/tarball/${arch}/${codename}/ | \
grep tar.gz | grep ${flavour} | sort -r | head -n 1 | \
awk '{print $2}')
if [ "x${file}" = "x" ]; then
file="https://cloud-images.ubuntu.com/${codename}/current/${codename}-server-cloudimg-${arch}.squashfs"
fi
echo ${file}
}
do_download_tarball() {
local url=${1}
local tarball=${2}
local download=true
[ "x${url}" = "x" ] && panic "unknown url file"
[ "x${tarball}" = "x" ] && panic "unknown tarball file"
case ${tarball} in
*.tar.gz)
if [ -f ${tarball} ]; then
wget ${url%.tar.gz}.md5sums.txt --quiet -O .foo.md5sums.txt || \
panic "failed to download MD5SUM file '${rul}'"
hash_remote=$(cat .foo.md5sums.txt | \
grep $(basename ${tarball}) | awk '{print $1}')
hash_local=$(md5sum ${tarball} | awk '{print $1}')
if [ "${hash_remote}" = "${hash_local}" ]; then
download=false
else
rm -f ${tarball}
fi
fi
;;
*)
if [ -f ${tarball} ]; then
download=false
fi
;;
esac
if [ "${download}" = true ]; then
wget ${url} -O ${tarball} || \
panic "failed to download tarball '${url}'"
fi
}
do_create_image() {
local baseimage=${1}
local tarball=${2}
[ "x${baseimage}" = "x" ] && panic "no given image file name"
[ "x${tarball}" = "x" ] && panic "no given tarball"
umount ${boot_mnt} ${rootfs_mnt} 2>/dev/null
rm -rf ${boot_mnt} ${rootfs_mnt}
mkdir -p ${boot_mnt} ${rootfs_mnt}
do_extract ${tarball}
do_mount
do_preinstall
do_run_fixups
do_postinstall
do_cleanup
do_unmount
if [ "x${LIVESYSTEM}" = "xtrue" ]; then
do_create_squashfs ${baseimage}
else
do_create_partition ${baseimage} 256
fi
}
download_uboot() {
echo "U-Boot won't work this build."
}
do_flash_bootloader() {
local disk=${1}
local uboot_tarball=${download_dir}/u-boot.tar.gz
echo "I: Downloading U-boot binaries"
download_uboot ${uboot_tarball} \
|| panic "failed to download bootloader release"
if [ -f ${uboot_tarball} ]; then
rm -rf ${download_dir}/sd_fuse
tar xzvf ${uboot_tarball} -C ${download_dir} || false
if [ -f ${download_dir}/sd_fuse/sd_fusing.sh ]; then
(cd ${download_dir}/sd_fuse;
sed -i "/eject/d" ./sd_fusing.sh;
chmod +x ./sd_fusing.sh;
./sd_fusing.sh ${disk} 2>/dev/null
)
fi
fi
}
do_finalize_image() {
local disk=${1}
local osimage=${2}.img
[ "x${PUBLISH}" = "xtrue" ] && COMPRESS=true
if [ -f ${disk} ]; then
echo "I: finalizing OS image - '${osimage}'"
mv -f ${disk} ${osimage} || panic "failed to target image file"
md5sum ${osimage} > ${osimage}.md5sum
if [ "x${COMPRESS}" = "xtrue" ]; then
echo "I: compressing OS image - '${osimage}'"
rm -f ${osimage}.xz
xz --verbose ${osimage} || panic "failed to create compressed image file"
md5sum ${osimage}.xz > ${osimage}.xz.md5sum
fi
if [ "x${ISOIMAGE}" = "xtrue" ]; then
mkisofs -R -relaxed-filenames -joliet-long -iso-level 3 -l \
-o ${2}.iso ${boot_mnt}
md5sum ${2}.iso > ${2}.iso.md5sum
fi
sed -i -r "s/ .*\/(.+)/ \1/g" ${osimage}*.md5sum
fi
}
do_custom_installer() {
local download_dir=${1}
local out_dir=${2}
mkdir -p ${download_dir} ${out_dir}
echo "I: querying decent base files for ${ARCH}/${DISTRO}/${FLAVOUR}"
url=$(do_query_latest_tarball http://ppa.linuxfactory.or.kr/images \
${ARCH} ${DISTRO} ${FLAVOUR})
[ "x${url}" = "x" ] && panic "Invalid URL - ${url}"
tarball=${download_dir}/$(basename ${url})
do_download_tarball ${url} ${tarball}
do_create_image ${TARGET_DEVICE} ${tarball}
case $(readlink -f ${TARGET_DEVICE}) in
/dev/sd*)
echo "W: bootloader won't be flashed to USB storage"
;;
*)
if [ -b ${TARGET_DEVICE} ]; then
disk=${TARGET_DEVICE}
else
disk=${LOOPBACK}
fi
do_flash_bootloader ${disk}
do_finalize_image ${TARGET_DEVICE} ${OUTFILE}
if [ "x${PUBLISH}" = "xtrue" ]; then
do_publish "192.168.0.2" \
"/srv/ppa.linuxfactory.or.kr/html/images/raw" \
${OUTFILE}
fi
;;
esac
}
do_publish() {
local host=${1}
local dir=${2}/${ARCH}/${DISTRO}
local osimage=${3}
files=`ls ${osimage}.*`
if [ ! "x${files}" = "x" ]; then
ssh ${host} mkdir -p ${dir} && \
scp ${files} ${host}:/${dir} || \
panic "failed to connect to remote server"
fi
}
# vim: set ft=sh ts=4 sw=4 expandtab: