OSDN Git Service

Merge "Upgrade to mksh R53a." am: c19d20531c am: 8b302a0123 am: c8ed698e56
[android-x86/external-mksh.git] / src / dot.mkshrc
1 # $Id$
2 # $MirOS: src/bin/mksh/dot.mkshrc,v 1.108 2016/07/26 22:03:41 tg Exp $
3 #-
4 # Copyright (c) 2002, 2003, 2004, 2006, 2007, 2008, 2009, 2010,
5 #               2011, 2012, 2013, 2014, 2015, 2016
6 #       mirabilos <m@mirbsd.org>
7 #
8 # Provided that these terms and disclaimer and all copyright notices
9 # are retained or reproduced in an accompanying document, permission
10 # is granted to deal in this work without restriction, including un-
11 # limited rights to use, publicly perform, distribute, sell, modify,
12 # merge, give away, or sublicence.
13 #
14 # This work is provided "AS IS" and WITHOUT WARRANTY of any kind, to
15 # the utmost extent permitted by applicable law, neither express nor
16 # implied; without malicious intent or gross negligence. In no event
17 # may a licensor, author or contributor be held liable for indirect,
18 # direct, other damage, loss, or other issues arising in any way out
19 # of dealing in the work, even if advised of the possibility of such
20 # damage or existence of a defect, except proven that it results out
21 # of said person's immediate fault when using the work as intended.
22 #-
23 # ${ENV:-~/.mkshrc}: mksh initialisation file for interactive shells
24
25 # catch non-mksh (including lksh) trying to run this file
26 case ${KSH_VERSION:-} in
27 *MIRBSD\ KSH*) ;;
28 *) return 0 ;;
29 esac
30
31 PS1='#'; (( USER_ID )) && PS1='$'; \: "${TERM:=vt100}${HOSTNAME:=$(\ulimit -c \
32     0; hostname 2>/dev/null)}${EDITOR:=/bin/ed}${USER:=$(\ulimit -c 0; id -un \
33     2>/dev/null || \echo \?)}${MKSH:=$(\builtin whence -p mksh)}"
34 HOSTNAME=${HOSTNAME%%*([         ]).*}; HOSTNAME=${HOSTNAME##*([         ])}
35 [[ $HOSTNAME = ?(ip6-)localhost?(6) ]] && HOSTNAME=
36 \: "${HOSTNAME:=nil}${MKSH:=/bin/mksh}"; \export EDITOR HOSTNAME MKSH TERM USER
37 PS4='[$EPOCHREALTIME] '; PS1=$'\001\r''${|
38         \typeset e=$?
39
40         (( e )) && REPLY+="$e|"
41         REPLY+=${USER}@${HOSTNAME%%.*}:
42
43         \typeset d=${PWD:-?}/ p=~; [[ $p = ?(*/) ]] || d=${d/#$p\//\~/}
44         d=${d%/}; \typeset m=${%d} n p=...; (( m > 0 )) || m=${#d}
45         (( m > (n = (COLUMNS/3 < 7 ? 7 : COLUMNS/3)) )) && d=${d:(-n)} || p=
46         REPLY+=$p$d
47
48         \return $e
49 } '"$PS1 "
50 \alias ls=ls
51 \unalias ls
52 \alias l='ls -F'
53 \alias la='l -a'
54 \alias ll='l -l'
55 \alias lo='l -alo'
56 \alias doch='sudo mksh -c "$(\builtin fc -ln -1)"'
57 \command -v rot13 >/dev/null || \alias rot13='tr \
58     abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ \
59     nopqrstuvwxyzabcdefghijklmNOPQRSTUVWXYZABCDEFGHIJKLM'
60 if \command -v hd >/dev/null; then \:; elif \command -v hexdump >/dev/null; then
61         function hd {
62                 hexdump -e '"%08.8_ax  " 8/1 "%02X " " - " 8/1 "%02X "' \
63                     -e '"  |" "%_p"' -e '"|\n"' "$@"
64         }
65 else
66         function hd {
67                 \typeset -Uui16 -Z11 pos=0
68                 \typeset -Uui16 -Z5 hv=2147483647
69                 \typeset dasc line i
70                 \set +U
71
72                 \cat "$@" | if \read -arN -1 line; then
73                         \typeset -i1 'line[*]'
74                         i=0
75                         while (( i < ${#line[*]} )); do
76                                 hv=${line[i++]}
77                                 if (( (pos & 15) == 0 )); then
78                                         (( pos )) && \
79                                             \builtin print -r -- "$dasc|"
80                                         \builtin print -n "${pos#16#}  "
81                                         dasc=' |'
82                                 fi
83                                 \builtin print -n "${hv#16#} "
84                                 #XXX EBCDIC, but we need [[:print:]] to fix this
85                                 if (( (hv < 32) || (hv > 126) )); then
86                                         dasc+=.
87                                 else
88                                         dasc+=${line[i-1]#1#}
89                                 fi
90                                 (( (pos++ & 15) == 7 )) && \
91                                     \builtin print -n -- '- '
92                         done
93                         while (( pos & 15 )); do
94                                 \builtin print -n '   '
95                                 (( (pos++ & 15) == 7 )) && \
96                                     \builtin print -n -- '- '
97                         done
98                         (( hv == 2147483647 )) || \builtin print -r -- "$dasc|"
99                 fi
100         }
101 fi
102
103 # Berkeley C shell compatible dirs, popd, and pushd functions
104 # Z shell compatible chpwd() hook, used to update DIRSTACK[0]
105 DIRSTACKBASE=$(\builtin realpath ~/. 2>/dev/null || \
106     \builtin print -nr -- "${HOME:-/}")
107 set -A DIRSTACK
108 function chpwd {
109         DIRSTACK[0]=$(\builtin realpath . 2>/dev/null || \
110             \builtin print -r -- "$PWD")
111         [[ $DIRSTACKBASE = ?(*/) ]] || \
112             DIRSTACK[0]=${DIRSTACK[0]/#$DIRSTACKBASE/\~}
113         \:
114 }
115 \chpwd .
116 cd() {
117         \builtin cd "$@" || \return $?
118         \chpwd "$@"
119 }
120 function cd_csh {
121         \typeset d t=${1/#\~/$DIRSTACKBASE}
122
123         if ! d=$(\builtin cd "$t" 2>&1); then
124                 \builtin print -u2 "${1}: ${d##*cd: $t: }."
125                 \return 1
126         fi
127         \cd "$t"
128 }
129 function dirs {
130         \typeset d dwidth
131         \typeset -i fl=0 fv=0 fn=0 cpos=0
132
133         while \getopts ":lvn" d; do
134                 case $d {
135                 (l)     fl=1 ;;
136                 (v)     fv=1 ;;
137                 (n)     fn=1 ;;
138                 (*)     \builtin print -u2 'Usage: dirs [-lvn].'
139                         \return 1 ;;
140                 }
141         done
142         \shift $((OPTIND - 1))
143         if (( $# > 0 )); then
144                 \builtin print -u2 'Usage: dirs [-lvn].'
145                 \return 1
146         fi
147         if (( fv )); then
148                 fv=0
149                 while (( fv < ${#DIRSTACK[*]} )); do
150                         d=${DIRSTACK[fv]}
151                         (( fl )) && d=${d/#\~/$DIRSTACKBASE}
152                         \builtin print -r -- "$fv       $d"
153                         \builtin let fv++
154                 done
155         else
156                 fv=0
157                 while (( fv < ${#DIRSTACK[*]} )); do
158                         d=${DIRSTACK[fv]}
159                         (( fl )) && d=${d/#\~/$DIRSTACKBASE}
160                         (( dwidth = (${%d} > 0 ? ${%d} : ${#d}) ))
161                         if (( fn && (cpos += dwidth + 1) >= 79 && \
162                             dwidth < 80 )); then
163                                 \builtin print
164                                 (( cpos = dwidth + 1 ))
165                         fi
166                         \builtin print -nr -- "$d "
167                         \builtin let fv++
168                 done
169                 \builtin print
170         fi
171         \return 0
172 }
173 function popd {
174         \typeset d fa
175         \typeset -i n=1
176
177         while \getopts ":0123456789lvn" d; do
178                 case $d {
179                 (l|v|n) fa+=" -$d" ;;
180                 (+*)    n=2
181                         \break ;;
182                 (*)     \builtin print -u2 'Usage: popd [-lvn] [+<n>].'
183                         \return 1 ;;
184                 }
185         done
186         \shift $((OPTIND - n))
187         n=0
188         if (( $# > 1 )); then
189                 \builtin print -u2 popd: Too many arguments.
190                 \return 1
191         elif [[ $1 = ++([0-9]) && $1 != +0 ]]; then
192                 if (( (n = ${1#+}) >= ${#DIRSTACK[*]} )); then
193                         \builtin print -u2 popd: Directory stack not that deep.
194                         \return 1
195                 fi
196         elif [[ -n $1 ]]; then
197                 \builtin print -u2 popd: Bad directory.
198                 \return 1
199         fi
200         if (( ${#DIRSTACK[*]} < 2 )); then
201                 \builtin print -u2 popd: Directory stack empty.
202                 \return 1
203         fi
204         \unset DIRSTACK[n]
205         \set -A DIRSTACK -- "${DIRSTACK[@]}"
206         \cd_csh "${DIRSTACK[0]}" || \return 1
207         \dirs $fa
208 }
209 function pushd {
210         \typeset d fa
211         \typeset -i n=1
212
213         while \getopts ":0123456789lvn" d; do
214                 case $d {
215                 (l|v|n) fa+=" -$d" ;;
216                 (+*)    n=2
217                         \break ;;
218                 (*)     \builtin print -u2 'Usage: pushd [-lvn] [<dir>|+<n>].'
219                         \return 1 ;;
220                 }
221         done
222         \shift $((OPTIND - n))
223         if (( $# == 0 )); then
224                 if (( ${#DIRSTACK[*]} < 2 )); then
225                         \builtin print -u2 pushd: No other directory.
226                         \return 1
227                 fi
228                 d=${DIRSTACK[1]}
229                 DIRSTACK[1]=${DIRSTACK[0]}
230                 \cd_csh "$d" || \return 1
231         elif (( $# > 1 )); then
232                 \builtin print -u2 pushd: Too many arguments.
233                 \return 1
234         elif [[ $1 = ++([0-9]) && $1 != +0 ]]; then
235                 if (( (n = ${1#+}) >= ${#DIRSTACK[*]} )); then
236                         \builtin print -u2 pushd: Directory stack not that deep.
237                         \return 1
238                 fi
239                 while (( n-- )); do
240                         d=${DIRSTACK[0]}
241                         \unset DIRSTACK[0]
242                         \set -A DIRSTACK -- "${DIRSTACK[@]}" "$d"
243                 done
244                 \cd_csh "${DIRSTACK[0]}" || \return 1
245         else
246                 \set -A DIRSTACK -- placeholder "${DIRSTACK[@]}"
247                 \cd_csh "$1" || \return 1
248         fi
249         \dirs $fa
250 }
251
252 # pager (not control character safe)
253 smores() (
254         \set +m
255         \cat "$@" |&
256         \trap "rv=\$?; 'kill' $! >/dev/null 2>&1; 'exit' \$rv" EXIT
257         while IFS= \read -pr line; do
258                 llen=${%line}
259                 (( llen == -1 )) && llen=${#line}
260                 (( llen = llen ? (llen + COLUMNS - 1) / COLUMNS : 1 ))
261                 if (( (curlin += llen) >= LINES )); then
262                         \builtin print -n -- '\e[7m--more--\e[0m'
263                         \read -u1 || \exit $?
264                         [[ $REPLY = [Qq]* ]] && \exit 0
265                         curlin=$llen
266                 fi
267                 \builtin print -r -- "$line"
268         done
269 )
270
271 # base64 encoder and decoder, RFC compliant, NUL safe, not EBCDIC safe
272 function Lb64decode {
273         \set +U
274         \typeset c s="$*" t
275         [[ -n $s ]] || { s=$(\cat; \builtin print x); s=${s%x}; }
276         \typeset -i i=0 j=0 n=${#s} p=0 v x
277         \typeset -i16 o
278
279         while (( i < n )); do
280                 c=${s:(i++):1}
281                 case $c {
282                 (=)     \break ;;
283                 ([A-Z]) (( v = 1#$c - 65 )) ;;
284                 ([a-z]) (( v = 1#$c - 71 )) ;;
285                 ([0-9]) (( v = 1#$c + 4 )) ;;
286                 (+)     v=62 ;;
287                 (/)     v=63 ;;
288                 (*)     \continue ;;
289                 }
290                 (( x = (x << 6) | v ))
291                 case $((p++)) {
292                 (0)     \continue ;;
293                 (1)     (( o = (x >> 4) & 255 )) ;;
294                 (2)     (( o = (x >> 2) & 255 )) ;;
295                 (3)     (( o = x & 255 ))
296                         p=0
297                         ;;
298                 }
299                 t+=\\x${o#16#}
300                 (( ++j & 4095 )) && \continue
301                 \builtin print -n $t
302                 t=
303         done
304         \builtin print -n $t
305 }
306
307 \set -A Lb64encode_tbl -- A B C D E F G H I J K L M N O P Q R S T U V W X Y Z \
308     a b c d e f g h i j k l m n o p q r s t u v w x y z 0 1 2 3 4 5 6 7 8 9 + /
309 function Lb64encode {
310         \set +U
311         \typeset c s t
312         if (( $# )); then
313                 \read -raN-1 s <<<"$*"
314                 \unset s[${#s[*]}-1]
315         else
316                 \read -raN-1 s
317         fi
318         \typeset -i i=0 n=${#s[*]} j v
319
320         while (( i < n )); do
321                 (( v = s[i++] << 16 ))
322                 (( j = i < n ? s[i++] : 0 ))
323                 (( v |= j << 8 ))
324                 (( j = i < n ? s[i++] : 0 ))
325                 (( v |= j ))
326                 t+=${Lb64encode_tbl[v >> 18]}${Lb64encode_tbl[v >> 12 & 63]}
327                 c=${Lb64encode_tbl[v >> 6 & 63]}
328                 if (( i <= n )); then
329                         t+=$c${Lb64encode_tbl[v & 63]}
330                 elif (( i == n + 1 )); then
331                         t+=$c=
332                 else
333                         t+===
334                 fi
335                 if (( ${#t} == 76 || i >= n )); then
336                         \builtin print $t
337                         t=
338                 fi
339         done
340 }
341
342 # Better Avalanche for the Jenkins Hash
343 \typeset -Z11 -Uui16 Lbafh_v
344 function Lbafh_init {
345         Lbafh_v=0
346 }
347 function Lbafh_add {
348         \set +U
349         \typeset s
350         if (( $# )); then
351                 \read -raN-1 s <<<"$*"
352                 \unset s[${#s[*]}-1]
353         else
354                 \read -raN-1 s
355         fi
356         \typeset -i i=0 n=${#s[*]}
357
358         while (( i < n )); do
359                 ((# Lbafh_v = (Lbafh_v + s[i++] + 1) * 1025 ))
360                 ((# Lbafh_v ^= Lbafh_v >> 6 ))
361         done
362 }
363 function Lbafh_finish {
364         \typeset -Ui t
365
366         ((# t = (((Lbafh_v >> 7) & 0x01010101) * 0x1B) ^ \
367             ((Lbafh_v << 1) & 0xFEFEFEFE) ))
368         ((# Lbafh_v = t ^ (t ^> 8) ^ (Lbafh_v ^> 8) ^ \
369             (Lbafh_v ^> 16) ^ (Lbafh_v ^> 24) ))
370         \:
371 }
372
373 # strip comments (and leading/trailing whitespace if IFS is set) from
374 # any file(s) given as argument, or stdin if none, and spew to stdout
375 function Lstripcom {
376         \set -o noglob
377         \cat "$@" | while \read _line; do
378                 _line=${_line%%#*}
379                 [[ -n $_line ]] && \builtin print -r -- $_line
380         done
381 }
382
383 # give MidnightBSD's laffer1 a bit of csh feeling
384 function setenv {
385         if (( $# )); then
386                 \eval '\export "$1"="${2:-}"'
387         else
388                 \typeset -x
389         fi
390 }
391
392 # toggle built-in aliases and utilities, and aliases and functions from mkshrc
393 function enable {
394         \typeset doprnt=0 mode=1 x y z rv=0
395         \typeset b_alias i_alias i_func nalias=0 nfunc=0 i_all
396         \set -A b_alias
397         \set -A i_alias
398         \set -A i_func
399
400         # accumulate mksh built-in aliases, in ASCIIbetical order
401         i_alias[nalias]=autoload; b_alias[nalias++]='\typeset -fu'
402         i_alias[nalias]=functions; b_alias[nalias++]='\typeset -f'
403         i_alias[nalias]=hash; b_alias[nalias++]='\builtin alias -t'
404         i_alias[nalias]=history; b_alias[nalias++]='\builtin fc -l'
405         i_alias[nalias]=integer; b_alias[nalias++]='\typeset -i'
406         i_alias[nalias]=local; b_alias[nalias++]='\typeset'
407         i_alias[nalias]=login; b_alias[nalias++]='\exec login'
408         i_alias[nalias]=nameref; b_alias[nalias++]='\typeset -n'
409         i_alias[nalias]=nohup; b_alias[nalias++]='nohup '
410         i_alias[nalias]=r; b_alias[nalias++]='\builtin fc -e -'
411         i_alias[nalias]=type; b_alias[nalias++]='\builtin whence -v'
412
413         # accumulate mksh built-in utilities, in definition order, even ifndef
414         i_func[nfunc++]=.
415         i_func[nfunc++]=:
416         i_func[nfunc++]='['
417         i_func[nfunc++]=alias
418         i_func[nfunc++]=break
419         i_func[nfunc++]=builtin
420         i_func[nfunc++]=cat
421         i_func[nfunc++]=cd
422         i_func[nfunc++]=chdir
423         i_func[nfunc++]=command
424         i_func[nfunc++]=continue
425         i_func[nfunc++]=echo
426         i_func[nfunc++]=eval
427         i_func[nfunc++]=exec
428         i_func[nfunc++]=exit
429         i_func[nfunc++]=export
430         i_func[nfunc++]=false
431         i_func[nfunc++]=fc
432         i_func[nfunc++]=getopts
433         i_func[nfunc++]=global
434         i_func[nfunc++]=jobs
435         i_func[nfunc++]=kill
436         i_func[nfunc++]=let
437         i_func[nfunc++]='let]'
438         i_func[nfunc++]=print
439         i_func[nfunc++]=pwd
440         i_func[nfunc++]=read
441         i_func[nfunc++]=readonly
442         i_func[nfunc++]=realpath
443         i_func[nfunc++]=rename
444         i_func[nfunc++]=return
445         i_func[nfunc++]=set
446         i_func[nfunc++]=shift
447         i_func[nfunc++]=source
448         i_func[nfunc++]=suspend
449         i_func[nfunc++]=test
450         i_func[nfunc++]=times
451         i_func[nfunc++]=trap
452         i_func[nfunc++]=true
453         i_func[nfunc++]=typeset
454         i_func[nfunc++]=ulimit
455         i_func[nfunc++]=umask
456         i_func[nfunc++]=unalias
457         i_func[nfunc++]=unset
458         i_func[nfunc++]=wait
459         i_func[nfunc++]=whence
460         i_func[nfunc++]=bg
461         i_func[nfunc++]=fg
462         i_func[nfunc++]=bind
463         i_func[nfunc++]=mknod
464         i_func[nfunc++]=printf
465         i_func[nfunc++]=sleep
466         i_func[nfunc++]=domainname
467         i_func[nfunc++]=extproc
468
469         # accumulate aliases from dot.mkshrc, in definition order
470         i_alias[nalias]=l; b_alias[nalias++]='ls -F'
471         i_alias[nalias]=la; b_alias[nalias++]='l -a'
472         i_alias[nalias]=ll; b_alias[nalias++]='l -l'
473         i_alias[nalias]=lo; b_alias[nalias++]='l -alo'
474         i_alias[nalias]=doch; b_alias[nalias++]='sudo mksh -c "$(\builtin fc -ln -1)"'
475         i_alias[nalias]=rot13; b_alias[nalias++]='tr abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ nopqrstuvwxyzabcdefghijklmNOPQRSTUVWXYZABCDEFGHIJKLM'
476         i_alias[nalias]=cls; b_alias[nalias++]='\builtin print -n \\ec'
477
478         # accumulate functions from dot.mkshrc, in definition order
479         i_func[nfunc++]=hd
480         i_func[nfunc++]=chpwd
481         i_func[nfunc++]=cd
482         i_func[nfunc++]=cd_csh
483         i_func[nfunc++]=dirs
484         i_func[nfunc++]=popd
485         i_func[nfunc++]=pushd
486         i_func[nfunc++]=smores
487         i_func[nfunc++]=Lb64decode
488         i_func[nfunc++]=Lb64encode
489         i_func[nfunc++]=Lbafh_init
490         i_func[nfunc++]=Lbafh_add
491         i_func[nfunc++]=Lbafh_finish
492         i_func[nfunc++]=Lstripcom
493         i_func[nfunc++]=setenv
494         i_func[nfunc++]=enable
495
496         # collect all identifiers, sorted ASCIIbetically
497         \set -sA i_all -- "${i_alias[@]}" "${i_func[@]}"
498
499         # handle options, we don't do dynamic loading
500         while \getopts "adf:nps" x; do
501                 case $x {
502                 (a)
503                         mode=-1
504                         ;;
505                 (d)
506                         # deliberately causing an error, like bash-static
507                         ;|
508                 (f)
509                         \builtin print -u2 enable: dynamic loading not available
510                         \return 2
511                         ;;
512                 (n)
513                         mode=0
514                         ;;
515                 (p)
516                         doprnt=1
517                         ;;
518                 (s)
519                         \set -sA i_all -- . : break continue eval exec exit \
520                             export readonly return set shift times trap unset
521                         ;;
522                 (*)
523                         \builtin print -u2 enable: usage: \
524                             "enable [-adnps] [-f filename] [name ...]"
525                         return 2
526                         ;;
527                 }
528         done
529         \shift $((OPTIND - 1))
530
531         # display builtins enabled/disabled/all/special?
532         if (( doprnt || ($# == 0) )); then
533                 for x in "${i_all[@]}"; do
534                         y=$(\alias "$x") || y=
535                         [[ $y = "$x='\\builtin whence -p $x >/dev/null || (\\builtin print mksh: $x: not found; exit 127) && \$(\\builtin whence -p $x)'" ]]; z=$?
536                         case $mode:$z {
537                         (-1:0|0:0)
538                                 \print -r -- "enable -n $x"
539                                 ;;
540                         (-1:1|1:1)
541                                 \print -r -- "enable $x"
542                                 ;;
543                         }
544                 done
545                 \return 0
546         fi
547
548         for x in "$@"; do
549                 z=0
550                 for y in "${i_alias[@]}" "${i_func[@]}"; do
551                         [[ $x = "$y" ]] || \continue
552                         z=1
553                         \break
554                 done
555                 if (( !z )); then
556                         \builtin print -ru2 enable: "$x": not a shell builtin
557                         rv=1
558                         \continue
559                 fi
560                 if (( !mode )); then
561                         # disable this
562                         \alias "$x=\\builtin whence -p $x >/dev/null || (\\builtin print mksh: $x: not found; exit 127) && \$(\\builtin whence -p $x)"
563                 else
564                         # find out if this is an alias or not, first
565                         z=0
566                         y=-1
567                         while (( ++y < nalias )); do
568                                 [[ $x = "${i_alias[y]}" ]] || \continue
569                                 z=1
570                                 \break
571                         done
572                         if (( z )); then
573                                 # re-enable the original alias body
574                                 \alias "$x=${b_alias[y]}"
575                         else
576                                 # re-enable the original utility/function
577                                 \unalias "$x"
578                         fi
579                 fi
580         done
581         \return $rv
582 }
583
584 \: place customisations below this line
585
586 for p in ~/.etc/bin ~/bin; do
587         [[ -d $p/. ]] || \continue
588         #XXX OS/2
589         [[ :$PATH: = *:$p:* ]] || PATH=$p:$PATH
590 done
591
592 \export SHELL=$MKSH MANWIDTH=80 LESSHISTFILE=-
593 \alias cls='\builtin print -n \\ec'
594
595 #\unset LANGUAGE LC_ADDRESS LC_ALL LC_COLLATE LC_IDENTIFICATION LC_MONETARY \
596 #    LC_NAME LC_NUMERIC LC_TELEPHONE LC_TIME
597 #p=en_GB.UTF-8
598 #\export LANG=C LC_CTYPE=$p LC_MEASUREMENT=$p LC_MESSAGES=$p LC_PAPER=$p
599 #\set -U
600
601 \unset p
602
603 \: place customisations above this line