1#!/usr/local/bin/bash
2
3# Copyright (c) 2015-2020 Oliver Mahmoudi
4# All rights reserved.
5#
6# Redistribution and use in source and binary forms, with or without
7# modification, are permitted providing that the following conditions
8# are met:
9# 1. Redistributions of source code must retain the above copyright
10#    notice, this list of conditions and the following disclaimer.
11# 2. Redistributions in binary form must reproduce the above copyright
12#    notice, this list of conditions and the following disclaimer in the
13#    documentation and/or other materials provided with the distribution.
14#
15# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR
16# IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
17# WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
18# ARE DISCLAIMED.  IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY
19# DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
20# DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
21# OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
22# HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT,
23# STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING
24# IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
25# POSSIBILITY OF SUCH DAMAGE.
26
27# mapdir - A utility to map files and directory hierarchies
28
29# Functions:
30# readfile()
31# check_excludes()
32# process_new_dir()
33# log_entry()
34# get_file_stats()
35# get_filename()
36# pretty_output()
37# check_savefile_existence()
38# usage()
39
40# Global variables:
41READLINK=
42CHECKSUM=
43DATE=$(date +%m%d%Y)
44DEPTH=0
45SAVEFILE=
46STRLEN=
47EXCLUDES=
48
49# Global variables that are used for the statistics at the end of the program.
50DIRS=0
51UNREADABLE_DIRS=0
52FILES=0
53UNREADABLE_FILES=0
54BLOCK_SPECIALS=0
55CHARACTER_SPECIALS=0
56PIPES=0
57SOCKETS=0
58SYMBOLIC_LINKS=0
59UNKNOWN=0
60TOTAL_FILES=0
61SAVEIFS=$IFS			# save the current Internal Field Seperator
62IFS=$(echo -en "\n\b")	# and set a new one
63export LC_COLLATE=C
64
65# Global flags for getopts:
66d_flag=0				# dotglob flag
67e_flag=0				# exclude files flag
68e_list=					# list of files to be excluded
69f_flag=0				# omit startdir flag
70h_flag=0				# use sha256 flag
71n_flag=0				# no savefile flag
72p_flag=0				# display output as directory tree
73s_flag=0				# alternate savefile flag
74s_file=					# the alternate savefile itself
75t_flag=0				# print statistics flag
76x_flag=0				# excludes from file flag
77x_file=					# the excludes file itself
78
79#
80# This is the main function.
81#
82readfile()
83{
84	local _file _depth _psymbol _pnoes _pbinstring
85
86	_file=$1
87	_depth=$3
88	_psymbol=$4
89	_pnoes=$5
90	_pbinstring=$6
91
92	# Continue?
93	$(check_excludes $_file)
94	if [ $? -eq 1 ] ; then
95		return
96	fi
97
98	if [ -d $_file ]; then
99		if [ -r $_file ] && [ -x $_file ]; then
100			DIRS=$((DIRS+1))
101			TOTAL_FILES=$((TOTAL_FILES+1))
102			if [ $f_flag -eq 0 ]; then
103				log_entry $_file directory fullpath
104				process_new_dir	$_file
105			else
106				if [ $p_flag -eq 1 ]; then
107					pretty_output $_file $_psymbol $_pbinstring
108					process_new_dir $_file $_depth
109				elif [ $2 -eq 0 ]; then
110					log_entry $_file directory filename_only
111					process_new_dir	$_file
112				elif [ $2 -eq 1 ]; then
113					process_new_dir	$_file
114				fi
115			fi
116		else
117			UNREADABLE_DIRS=$((UNREADABLE_DIRS+1))
118			TOTAL_FILES=$((TOTAL_FILES+1))
119			if [ $f_flag -eq 0 ]; then
120				log_entry $_file "directory is not readable" fullpath
121			else
122				if [ $p_flag -eq 1 ]; then
123					pretty_output $_file $_psymbol $_pbinstring
124				elif [ $2 -eq 0 ]; then
125					log_entry $_file "directory is not readable" filename_only
126				fi
127			fi
128		fi
129	elif [ -b $_file ]; then
130		BLOCK_SPECIALS=$((BLOCK_SPECIALS+1))
131		TOTAL_FILES=$((TOTAL_FILES+1))
132		if [ $f_flag -eq 0 ]; then
133			log_entry $_file "block special file" fullpath
134		else
135			if [ $p_flag -eq 0 ]; then
136				log_entry $_file "block special file" filename_only
137			else
138				pretty_output $_file $_psymbol $_pbinstring
139			fi
140		fi
141	elif [ -c $_file ]; then
142		CHARACTER_SPECIALS=$((CHARACTER_SPECIALS+1))
143		TOTAL_FILES=$((TOTAL_FILES+1))
144		if [ $f_flag -eq 0 ]; then
145			log_entry $_file "character special file" fullpath
146		else
147			if [ $p_flag -eq 0 ]; then
148				log_entry $_file "character special file" filename_only
149			else
150				pretty_output $_file $_psymbol $_pbinstring
151			fi
152		fi
153	elif [ -L $_file ]; then
154		SYMBOLIC_LINKS=$((SYMBOLIC_LINKS+1))
155		TOTAL_FILES=$((TOTAL_FILES+1))
156		if [ $f_flag -eq 0 ]; then
157			log_entry $_file "symbolic link" fullpath
158		else
159			if [ $p_flag -eq 0 ]; then
160				log_entry $_file "symbolic link" filename_only
161			else
162				pretty_output $_file $_psymbol $_pbinstring
163			fi
164		fi
165	elif [ -f $_file ]; then
166		if [ -r $_file ]; then				# it's a readable file
167			FILES=$((FILES+1))
168			TOTAL_FILES=$((TOTAL_FILES+1))
169			if [ $f_flag -eq 0 ]; then
170				get_file_stats $_file readable fullpath
171			else
172				if [ $p_flag -eq 0 ]; then
173					get_file_stats $_file readable filename_only
174				else
175					pretty_output $_file $_psymbol $_pbinstring
176				fi
177			fi
178		else 								# unreadable file
179			UNREADABLE_FILES=$((UNREADABLE_FILES+1))
180			TOTAL_FILES=$((TOTAL_FILES+1))
181			if [ $f_flag -eq 0 ]; then
182				get_file_stats $_file unreadable fullpath
183			else
184				if [ $p_flag -eq 0 ]; then
185					get_file_stats $_file unreadable filename_only
186				else
187					pretty_output $_file $_psymbol $_pbinstring
188				fi
189			fi
190		fi
191	elif [ -p $_file ]; then
192		PIPES=$((PIPES+1))
193		TOTAL_FILES=$((TOTAL_FILES+1))
194		if [ $f_flag -eq 0 ]; then
195			log_entry $_file pipe fullpath
196		else
197			if [ $p_flag -eq 0 ]; then
198				log_entry $_file pipe filename_only
199			else
200				pretty_output $_file $_psymbol $_pbinstring
201			fi
202		fi
203	elif [ -S $_file ]; then
204		SOCKETS=$((SOCKETS+1))
205		TOTAL_FILES=$((TOTAL_FILES+1))
206		if [ $f_flag -eq 0 ]; then
207			log_entry $_file socket fullpath
208		else
209			if [ $p_flag -eq 0 ]; then
210				log_entry $_file socket filename_only
211			else
212				pretty_output $_file $_psymbol $_pbinstring
213			fi
214		fi
215	else
216		UNKNOWN=$((UNKNOWN+1))
217		TOTAL_FILES=$((TOTAL_FILES+1))
218		if [ $f_flag -eq 0 ]; then
219			log_entry $_file "unknown file type" fullpath
220		else
221			if [ $p_flag -eq 0 ]; then
222				log_entry $_file "unknown file type" filename_only
223			else
224				pretty_output $_file $_psymbol $_pbinstring
225			fi
226		fi
227	fi
228}
229
230#
231# Check for files that are to be excluded from the search process. Passed via -e and/or -x.
232#
233check_excludes()
234{
235	local _file_to_check
236
237	_file_to_check=$1
238
239	# We need the "standard" IFS to parse the array. Otherwise it won't work...
240	IFS=$SAVEIFS
241	for i in $EXCLUDES ; do
242		if [ "$_file_to_check" = "$i" ] ; then
243			IFS=$(echo -en "\n\b")
244			return 1
245		fi
246	done
247
248	IFS=$(echo -en "\n\b")
249	return 0
250}
251
252#
253# In case we encounter a new directory, this function gets called.
254# It checks whether or nor the new directory has contents and calls
255# readline again.
256#
257process_new_dir()
258{
259	local _dir _contents _depth _entry _newpath _noes _pcontents _pdir _pnoes _psstring
260
261	# For the pretty_output function, first examine all the parent directories,
262	# to find out what type of entries we have and set binary flags accordingly.
263	_depth=$2
264	if [ $p_flag -eq 1 ] && [ $_depth -ge 1 ]; then
265		_pdir=$1
266		while [ $_depth -ge 0 ]; do
267			cd $_pdir
268			cd ..
269			_pcontents=*
270			_pcontents=($_pcontents)
271			_pnoes=${#_pcontents[@]}
272			_pdir=$(get_filename $_pdir)
273
274			if [ "${_pcontents[$_pnoes-1]}" = "$_pdir" ]; then
275				_psstring="0$_psstring"
276			else
277				_psstring="1$_psstring"
278			fi
279
280			_pdir=$(pwd)
281			_depth=$(($_depth-1))
282		done
283	fi
284
285	# Now process the directory that actually got passed to the function.
286	_dir=$1
287	_depth=$2
288	cd $_dir
289	_contents=*
290	_noes=($_contents)
291	_noes=${#_noes[@]}
292
293	if [ $_noes -ne 0 ]; then
294		_depth=$(($_depth+1))
295
296		for _entry in $_contents ; do
297			_newpath="$_dir/$_entry"
298			if [ $_noes -eq 1 ]; then
299				readfile $_newpath 0 $_depth lastentry $_pnoes $_psstring
300			else
301				readfile $_newpath 0 $_depth middleentry $_pnoes $_psstring
302			fi
303			_noes=$(($_noes-1))
304		done
305	fi
306}
307
308#
309# Log filetype to stdout and to SAVEFILE if desired. This function logs all
310# filetypes except for regular files, which need a little more handling based
311# on OS and accessability.
312#
313log_entry()
314{
315	local _entry _filetype _logtype
316
317	_filetype=$2
318	_logtype=$3
319
320	if [ "$_logtype" = "fullpath" ]; then
321		_entry=$1
322	elif [ "$_logtype" = "filename_only" ]; then
323		_entry=$(get_filename $1)
324	fi
325
326	echo $_entry - $_filetype
327	if [ $n_flag -eq 0 ]; then
328		echo $_entry - $_filetype >> $SAVEFILE
329	fi
330}
331
332#
333# This function can be considered to be the log_entry function for regular files.
334#
335get_file_stats()
336{
337	local _file
338
339	_file=$1
340
341	if [ "$2" = "readable" ] && [ "$3" = "fullpath" ]; then
342		echo $_file - regular file - Size: `ls -l $_file | awk '{ print $5 }'` bytes - \
343		${CHECKSUM}: `${CHECKSUM} $_file | awk '{ print $4 }'`
344			if [ $n_flag -eq 0 ]; then
345				echo $_file - regular file - Size: `ls -l $_file | awk '{ print $5 }'` bytes - \
346				${CHECKSUM}: `${CHECKSUM} $_file | awk '{ print $4 }'` >> $SAVEFILE
347			fi
348	elif [ "$2" = "readable" ] && [ "$3" = "filename_only" ]; then
349		echo $(get_filename $_file) - regular file - Size: `ls -l $_file | awk '{ print $5 }'` bytes - \
350			${CHECKSUM}: `${CHECKSUM} $_file | awk '{ print $4 }'`
351		if [ $n_flag -eq 0 ]; then
352			echo $(get_filename $_file) - regular file - Size: `ls -l $_file | awk '{ print $5 }'` bytes - \
353				${CHECKSUM}: `${CHECKSUM} $_file | awk '{ print $4 }'` >> $SAVEFILE
354		fi
355	elif ["$2" = "unreadable" ] && [ "$3" = "fullpath" ]; then
356		echo $_file - regular file - Size: `ls -l $_file | awk '{ print $5 }'` bytes - \
357			${CHECKSUM}: not readable
358		if [ $n_flag -eq 0 ]; then
359			echo $_file - regular file - Size: `ls -l $_file | awk '{ print $5 }'` bytes - \
360				${CHECKSUM}: not readable >> $SAVEFILE
361		fi
362	elif ["$2" = "unreadable" ] && [ "$3" = "filename_only" ]; then
363		echo $(get_filename $_file) - regular file - Size: `ls -l $_file | awk '{ print $5 }'` bytes - \
364			${CHECKSUM}: not readable
365		if [ $n_flag -eq 0 ]; then
366			echo $(get_filename $_file) - regular file - Size: `ls -l $_file | awk '{ print $5 }'` bytes - \
367				${CHECKSUM}: not readable >> $SAVEFILE
368		fi
369	fi
370}
371
372#
373# When invoking mapdir with the -f switch, we only log the filename.
374#
375get_filename()
376{
377	local _filename
378
379	# Use awk to get the last "/" character and extract the part to the right of it.
380	_filename=$(awk -v filename=$1 'BEGIN {
381		n = split(filename, a, "/");
382		print a[n];
383	}')
384
385	echo $_filename
386}
387
388#
389# Print the structure of the argument passed to mapdir to stdout as a pretty output tree.
390# Invoked with the -p switch. Needs -f to work.
391#
392pretty_output()
393{
394	local _entry _space  _symboltype _binstring _str _box_rh _box_hl _box_vl _box_mh
395
396	# http://jrgraphix.net/r/Unicode/2500-257F
397	_box_rh=$(echo -e "\u2514")		# └
398	_box_hl=$(echo -e "\u2500")		# ─
399	_box_vl=$(echo -e "\u2502")		# │
400	_box_mh=$(echo -e "\u251C")		# ├
401
402	_entry=$(get_filename $1)
403	_symboltype=$2
404	_binstring=$3
405	_space="  "
406	_str=""
407
408	# Examine the binstring and prepare the middle part of the entry.
409	if [ ! -z $_binstring ]; then
410		for i in $(seq 0 1 $((${#_binstring}-1))) ; do
411			if [ $i -eq 0 ]; then
412				continue
413			elif [ "${_binstring:i:1}" = "0" ]; then
414				_str="$_str "
415			elif [ "${_binstring:i:1}" = "1" ]; then
416				_str="$_str$_box_vl"
417			fi
418		done
419	fi
420
421	# Now prettyprint the entry.
422	if [ $TOTAL_FILES -eq 1 ]; then
423		echo "$_box_hl$_box_hl$_entry"
424		if [ $n_flag -eq 0 ]; then
425			echo "$_box_hl$_box_hl$_entry" >> $SAVEFILE
426		fi
427	elif [ "$_symboltype" = "middleentry" ]; then
428		echo "$_space$_str$_box_mh$_entry"
429		if [ $n_flag -eq 0 ]; then
430			echo "$_space$_str$_box_mh$_entry" >> $SAVEFILE
431		fi
432	elif [ "$_symboltype" = "lastentry" ]; then
433		echo "$_space$_str$_box_rh$_entry"
434		if [ $n_flag -eq 0 ]; then
435			echo "$_space$_str$_box_rh$_entry" >> $SAVEFILE
436		fi
437	fi
438}
439
440check_savefile_existence()
441{
442	local choice
443
444	if [ -e $1 ] ; then			# Confirm
445		echo -n "The savefile: $1 already exists. Do you want to overwrite it Yes/No? "
446		read -t 30 choice		# We got 30 seconds to make a choice
447
448		case $choice in
449
450		[Yy][Ee][Ss] | [Yy] )
451			return
452			;;
453		[Nn][Oo] | [Nn] )
454			echo 'Aborted!'
455			exit 1
456			;;
457		*)
458			echo "No input received. Terminating."
459			exit 1
460			;;
461		esac
462	fi
463}
464
465usage()
466{
467	echo "usage: mapdir [-dfhnpt] [-e excludes] [-s savefile] \
468[-x excludes_file] [file]||[directory]"
469	exit 1
470}
471
472### Point of entry ###
473while getopts ":de:fhnps:tx:" opt ; do
474	case $opt in
475		d)
476			d_flag=1
477			;;
478		e)
479			e_flag=1
480			e_list=$OPTARG
481			;;
482		f)
483			f_flag=1
484			;;
485		h)
486			h_flag=1
487			;;
488		n)
489			n_flag=1
490			;;
491		p)
492			p_flag=1
493			;;
494		s)
495			s_flag=1
496			s_file=$OPTARG
497			;;
498		t)
499			t_flag=1
500			;;
501		x)
502			x_flag=1
503			x_file=$OPTARG
504			;;
505		\?)
506			echo "unkown flag: -$OPTARG."
507			usage
508			exit
509			;;
510	esac
511done
512
513shift $((OPTIND-1))
514
515# If -p == 1 => -f == 1
516if [ $p_flag -eq 1 ] && [ $f_flag -eq 0 ] ; then
517	echo "The -p option can only be used with the -f option."
518	exit
519fi
520
521# Allowing (-p && -e) || (-p && -x) would break the formatting of the output tree.
522if ([ $p_flag -eq 1 ] && [ $e_flag -eq 1 ]) || ([ $p_flag -eq 1 ] && [ $x_flag -eq 1 ]) ; then
523	echo "The -p option cannot be used with the -e or -x options."
524	exit
525fi
526
527# Process the other options
528if [ $d_flag -eq 0 ]; then
529	shopt -s dotglob nullglob
530else
531	shopt -s nullglob
532fi
533
534if [ $h_flag -eq 1 ]; then				# Change CHECKSUM to sha256
535	CHECKSUM=sha256
536else
537	CHECKSUM=md5
538fi
539
540if [ $e_flag -eq 1 ]; then
541	for i in $e_list ; do
542		EXCLUDES="$i $EXCLUDES"
543	done
544fi
545
546if [ $x_flag -eq 1 ]; then
547	while read line
548	do
549		EXCLUDES="$line $EXCLUDES"
550	done < "$x_file"
551fi
552
553#
554# If an argument is given, take it, otherwise process the current directory.
555#
556if [ $# -eq 1 ]; then
557	READLINK=$(readlink -f $1)
558	if [ ! -e $READLINK ]; then
559 		echo "The file: $READLINK doesn\'t exist."
560		usage
561	fi
562	if [ $n_flag -eq 0 ]; then
563		if [ $s_flag -eq 0 ]; then
564			SAVEFILE=~/mapdir$(readlink -f $1 | sed s#/#_#g)_$DATE.txt
565			check_savefile_existence $SAVEFILE
566			: > $SAVEFILE
567		else
568			SAVEFILE=~/${s_file}
569			check_savefile_existence $SAVEFILE
570			: > $SAVEFILE
571		fi
572	fi
573else
574	READLINK=$(readlink -f ./)
575	if [ $n_flag -eq 0 ]; then
576		if [ $s_flag -eq 0 ]; then
577			SAVEFILE=~/mapdir$(pwd | sed s#/#_#g)_$DATE.txt
578			check_savefile_existence $SAVEFILE
579			:> $SAVEFILE
580		else
581			SAVEFILE=~/${s_file}
582			check_savefile_existence $SAVEFILE
583			: > $SAVEFILE
584		fi
585	fi
586fi
587
588# When calling the readline function for the first time, we pass a second argument
589# of "1" to it. This serves the purpose of pleasing the diff utility when invoking
590# mapdir with the -f switch and having a directory as the first argument. If we would
591# map the starting directory to the $SAVEFILE and would later on compare it with
592# another the $SAVEFILE, the diff utility would obviously exit with a return value
593# other than 0, even though the contents of the directories may be truly equivalent.
594# Consider for example the folders:
595#
596# /media/filesystem_a and /media/filesystem_b that both have the same content.
597#
598# The logic is as follows: if the file is a folder, then the readlink function detects
599# this in the "is directory" part and skips mapping its occurence to the $SAVEFILE.
600# For subsequent calls to readlink we will pass a second argument of "0" to the
601# function, which this time maps it.
602
603# Start processing the file/folder...
604readfile $READLINK 1 $DEPTH
605
606#
607# At this point, we are done parsing. Now print statistics if desired as per the -t flag.
608#
609if [ $t_flag -eq 1 ]; then
610	echo
611	if [ $n_flag -eq 0 ]; then
612		echo >> $SAVEFILE
613	fi
614
615	STRLEN="########## Statistics for $READLINK ##########"
616	echo $STRLEN
617	if [ $n_flag -eq 0 ]; then
618		if [ $f_flag -eq 0 ]; then
619			echo $STRLEN >> $SAVEFILE
620		else
621			echo "########## Statistics ##########" >> $SAVEFILE
622		fi
623	fi
624
625	if [ $DIRS -ne 0 ]; then
626		echo Number of directories: $DIRS
627		if [ $n_flag -eq 0 ]; then
628			echo Number of directories: $DIRS >> $SAVEFILE
629		fi
630	fi
631	if [ $UNREADABLE_DIRS -ne 0 ]; then
632		echo Number of unreadable directories: $UNREADABLE_DIRS
633		if [ $n_flag -eq 0 ]; then
634			echo Number of unreadable directories: $UNREADABLE_DIRS >> $SAVEFILE
635		fi
636	fi
637	if [ $FILES -ne 0 ]; then
638		echo Number of regular files: $FILES
639		if [ $n_flag -eq 0 ]; then
640			echo Number of regular files: $FILES >> $SAVEFILE
641		fi
642	fi
643	if [ $UNREADABLE_FILES -ne 0 ]; then
644		echo Number of unreadble files: $UNREADABLE_FILES
645		if [ $n_flag -eq 0 ]; then
646			echo Number of unreadble files: $UNREADABLE_FILES >> $SAVEFILE
647		fi
648	fi
649	if [ $BLOCK_SPECIALS -ne 0 ]; then
650		echo Number of block special files: $BLOCK_SPECIALS
651		if [ $n_flag -eq 0 ]; then
652			echo Number of block special files: $BLOCK_SPECIALS >> $SAVEFILE
653		fi
654	fi
655	if [ $CHARACTER_SPECIALS -ne 0 ]; then
656		echo Number of character speial files: $CHARACTER_SPECIAL
657		if [ $n_flag -eq 0 ]; then
658			echo Number of character speial files: $CHARACTER_SPECIAL >> $SAVEFILE
659		fi
660	fi
661	if [ $PIPES -ne 0 ]; then
662		echo Number of pipes: $PIPE
663		if [ $n_flag -eq 0 ]; then
664			echo Number of pipes: $PIPE >> $SAVEFILE
665		fi
666	fi
667	if [ $SOCKETS -ne 0 ]; then
668		echo Number of sockets: $SOCKET
669		if [ $n_flag -eq 0 ]; then
670			echo Number of sockets: $SOCKET >> $SAVEFILE
671		fi
672	fi
673	if [ $SYMBOLIC_LINKS -ne 0 ]; then
674		echo Number of symbolic links: $SYMBOLIC_LINKS
675		if [ $n_flag -eq 0 ]; then
676			echo Number of symbolic links: $SYMBOLIC_LINKS >> $SAVEFILE
677		fi
678	fi
679	if [ $UNKNOWN -ne 0 ]; then
680		echo Number of symbolic links: $UNKNOWN
681		if [ $n_flag -eq 0 ]; then
682			echo Number of symbolic links: $UNKNOWN >> $SAVEFILE
683		fi
684	fi
685	if [ $TOTAL_FILES -ne 0 ]; then
686		echo Total number of files: $TOTAL_FILES
687		if [ $n_flag -eq 0 ]; then
688			echo Total number of files: $TOTAL_FILES >> $SAVEFILE
689		fi
690	fi
691
692	# Formatted output
693	STRLEN=${#STRLEN}
694	while [ $STRLEN -gt 0 ]
695	do
696		echo -n "#"
697		if [ $n_flag -eq 0 ]; then
698			if [ $f_flag -eq 0 ]; then
699				echo -n "#" >> $SAVEFILE
700			fi
701		fi
702		STRLEN=$((STRLEN-1))
703	done
704	echo
705	if [ $n_flag -eq 0 ]; then
706		if [ $f_flag -eq 0 ]; then
707			echo >> $SAVEFILE
708		else
709			echo "################################" >> $SAVEFILE
710		fi
711	fi
712fi # t_flag
713
714IFS=$SAVEIFS			# reset the old IFS
715exit 0
716