Diverting

매크로 치환 외에 특정 영역을 나중 or 먼저 출력하거나 또는 출력에서 제외하는 등 출력 조절이 필요할 때가 있습니다. 이와 같은 기능을 제공하는 것이 diverting and undiverting 입니다.

divert

divert
divert ( Number )

M4 는 언제든지 출력을 임시 영역에 저장했다가 필요할 때 출력할 수 있습니다. 임시 영역은 양의 정수로 나타내는데 0 은 디폴트로 normal output stream 을 나타냅니다. 그러니까 divert(3) 으로 임시 영역 3 에 출력을 저장한 후에 divert(0) 을 하면 다시 normal output stream 으로 돌아와 출력되는것입니다. 저장한 내용을 나중에 직접 undivert 를 이용해 출력하지 않으면 자동으로 마지막에 undivert 되어 출력됩니다. 이때 출력은 번호순서에 따라 출력됩니다.

() 없는 divertdivert(0) 와 같습니다.

divert(-1)      send to /dev/null
divert(0)       send to stdout
divert(1)       send to buffer #1
divert(2)       send to buffer #2
divert(3)       send to buffer #3
. . .

아래는 활용예 입니다.

  • divert : divert(1) 에 의해 AAA, BBB 라인이 임시 영역 1 에 저장되고 라인 222 는 divert(0) 에 의해 normal output stream 으로 출력되므로 111, 222 라인이 먼저 출력되고 나중에 AAA, BBB 라인이 출력됩니다.
  • order : 라인 BBB 는 divert(2) 에 해당하고 라인 AAA 는 divert(3) 에 해당하므로 출력시 BBB, AAA 라인 순으로 출력됩니다.
  • append : 같은 divert 번호를 사용하면 내용이 append 됩니다. divert(4) 가 두 군데로 분리되어 있지만 내용이 append 되어 함께 출력됩니다.
  • discard : 출력을 저장할 때 사용할 수 있는 번호는 양의 정수이므로 음수는 출력을 discard 할때 사용됩니다. AAA, BBB 라인이 discard 되어 출력되지 않는 것을 볼 수 있습니다. 이때 출력만 discard 되는 것이지 내용중 매크로는 정상적으로 실행되므로 foo 매크로가 정의되어 100 이 출력됩니다.
# divert             # order              # append              # discard

$ m4 <<\@            $ m4 <<\@            $ m4 <<\@             $ m4 <<\@
111111111            111111111            divert(4)dnl          111111111
divert(1)dnl         divert(3)dnl         AAAAAAAAA             divert(-1)dnl
AAAAAAAAA            AAAAAAAAA            divert(0)dnl          AAAAAAAAA
BBBBBBBBB            divert(2)dnl         111111111             define(foo, 100)
divert(0)dnl         BBBBBBBBB            divert(4)dnl          BBBBBBBBB
222222222            divert(0)dnl         BBBBBBBBB             divert(0)dnl
@                    222222222            divert(0)dnl          222222222
                     @                    222222222             foo
111111111                                 @                     @
222222222            111111111                                  
AAAAAAAAA            222222222            111111111             111111111
BBBBBBBBB            BBBBBBBBB            222222222             222222222
                     AAAAAAAAA            AAAAAAAAA             100 
                                          BBBBBBBBB

다음과 같이 divert(-1) 를 활용하면 comment 와 매크로를 define 할 때 생성되는 trailing newline 들이 출력에서 제외됩니다. 마지막 forloop 매크로 확장 결과를 보면 공백 라인이 하나도 생성되지 않는 것을 볼 수 있습니다.

$ m4 <<\@
divert(`-1')
# forloop(var, from, to, stmt) - improved version:
#   works even if VAR is not a strict macro name
#   performs sanity check that FROM is larger than TO
#   allows complex numerical expressions in TO and FROM
define(`forloop', `ifelse(eval(`($2) <= ($3)'), `1',
  `pushdef(`$1')_$0(`$1', eval(`$2'),
    eval(`$3'), `$4')popdef(`$1')')')
define(`_forloop',
  `define(`$1', `$2')$4`'ifelse(`$2', `$3', `',
    `$0(`$1', incr(`$2'), `$3', `$4')')')
divert`'dnl                                    # divert`'dnl 은 divert(0)dnl 과 같은것임
forloop(`x', 1, 5, `[x] ')
@
[1] [2] [3] [4] [5]

undivert

undivert
undivert ( Number . . . )
undivert ( File . . . )

undivert 는 divert 로 저장해놓은 영역을 현재 divert 영역으로 출력할 때 사용합니다. 그러니까 divert(3) 으로 저장해놓은 영역을 normal output stream 으로 출력하려면 먼저 divert(0) 을 한 후에 undivert(3) 해야 하는 것입니다. ( 물론 현재 divert(0) 이면 먼저 divert(0) 할 필요는 없습니다 ). 또한 divert(3) 영역을 divert(2) 영역으로 출력할 수도 있는데 이때도 먼저 divert(2) 로 변경한 후에 undivert(3) 하면 됩니다.

번호는 여러 개를 입력할 수 있고 입력된 순서에 따라서 출력됩니다. () 없이 undivert 하면 현재 저장되어 있는 모든 divert 영역을 출력합니다. 한번 undivert 하면 해당 영역은 empty 가 되므로 다시 출력할 수 없습니다.

undivert 시 매크로 확장은 발생하지 않습니다.

undivert(1)           dump buffer #1 to the current data stream
undivert(2)           dump buffer #2 to the current data stream
undivert(3,1,2)       dump buffer #3, #1, #2 to the current data stream
undivert              dumps all buffers in numeric order
$ m4 <<\@            $ m4 <<\@             $ m4 <<\@             $ m4 <<\@
111111111            111111111             111111111             111111111
divert(3)dnl         divert(3)dnl          divert(3)dnl          divert(3)dnl
AAAAAAAAA            AAAAAAAAA             AAAAAAAAA             AAAAAAAAA
divert(2)dnl         divert(2)dnl          divert(2)dnl          divert(2)dnl
BBBBBBBBB            BBBBBBBBB             BBBBBBBBB             BBBBBBBBB
divert(1)dnl         divert(1)dnl          undivert(3)dnl        divert(1)dnl
CCCCCCCCC            CCCCCCCCC             divert(1)dnl          CCCCCCCCC
divert(0)dnl         divert(0)dnl          CCCCCCCCC             divert(0)dnl
222222222            undivert(3,1,2)dnl    divert(0)dnl          undivert`'dnl
@                    222222222             undivert(2,1)dnl      222222222
                     @                     222222222             @
111111111                                  @                     
222222222            111111111                                   111111111
CCCCCCCCC            AAAAAAAAA             111111111             CCCCCCCCC
BBBBBBBBB            CCCCCCCCC             BBBBBBBBB             BBBBBBBBB
AAAAAAAAA            BBBBBBBBB             AAAAAAAAA             AAAAAAAAA
                     222222222             CCCCCCCCC             222222222
                                           222222222

divert 영역 삭제

저장해놓은 divert 영역을 삭제하려면 먼저 divert(-1) 로 변경한 후에 undivert 하면 됩니다.

divert(0) 영역은 삭제되지 않습니다.

$ m4 <<\@            $ m4 <<\@             $ m4 <<\@             $ m4 <<\@      
111111111            111111111             111111111             111111111
divert(3)dnl         divert(3)dnl          divert(3)dnl          divert(3)dnl
AAAAAAAAA            AAAAAAAAA             AAAAAAAAA             AAAAAAAAA
divert(2)dnl         divert(2)dnl          divert(2)dnl          divert(2)dnl
BBBBBBBBB            BBBBBBBBB             BBBBBBBBB             BBBBBBBBB
divert(1)dnl         divert(1)dnl          divert(1)dnl          divert(-1)dnl
CCCCCCCCC            CCCCCCCCC             CCCCCCCCC             undivert(3)
divert(0)dnl         divert(0)dnl          divert(0)dnl          divert(1)dnl
222222222            222222222             222222222             CCCCCCCCC
@                    divert(-1)dnl         divert(-1)dnl         divert(0)dnl
                     undivert              undivert(3,2)         222222222
111111111            @                     @                     @
222222222                                                        
CCCCCCCCC            111111111             111111111             111111111
BBBBBBBBB            222222222             222222222             222222222
AAAAAAAAA                                  CCCCCCCCC             CCCCCCCCC
                                                                 BBBBBBBBB

활용 예제 )

$ m4 <<\EOF
divert(-1)

m4 has multiple output queues that can be manipulated with the
`divert' macro. Valid queues range from 0 to 10, inclusive, with
the default queue being 0. As an extension, GNU m4 supports more
diversions, limited only by integer type size.

Calling the `divert' macro with an invalid queue causes text to be
discarded until another call.  Note that even while output is being
discarded, quotes around `divert' and other macros are needed to
prevent expansion.

# Macros aren't expanded within comments, meaning that keywords such
# as divert and other built-ins may be used without consequence.

# HTML utility macro:

define(`H2_COUNT', 0)

# The H2_COUNT macro is redefined every time the H2 macro is used:

define(`H2',
    `define(`H2_COUNT', incr(H2_COUNT))<h2>H2_COUNT. $1</h2>')

divert(1)dnl
dnl
dnl The dnl macro causes m4 to discard the rest of the line, thus
dnl preventing unwanted blank lines from appearing in the output.
dnl
H2(First Section)
H2(Second Section)
H2(Conclusion)
dnl
divert(0)dnl
dnl
<HTML>
undivert(1)dnl One of the queues is being pushed to output.
</HTML>
EOF
--------------------------

<HTML>
<h2>1. First Section</h2>
<h2>2. Second Section</h2>
<h2>3. Conclusion</h2>
</HTML>

undivert 를 이용한 파일 include

undivert 를 이용해 파일도 include 할 수 있습니다. 기본적으로 undivert 시에는 매크로 확장이 안되므로 문자 그대로 파일 내용이 include 됩니다.

undivert(`foo.txt')              dump file `foo.txt' to the current data stream
undivert(`foo.txt',`bar.txt')    dump file `foo.txt', `bar.txt' . . .
$ cat /usr/share/doc/m4/examples/incl.m4       # include 에 사용되는 파일
Include file start
foo
Include file end
---------------------------------------

$ m4 <<\@ -I /usr/share/doc/m4/examples
define(`foo', `hello include')dnl
include(`incl.m4')dnl                      # include 매크로
@

Include file start
hello include                              # foo 매크로가 확장된다.
Include file end
---------------------------------------

$ m4 <<\@ -I /usr/share/doc/m4/examples
define(`foo', `hello include')dnl
undivert(`incl.m4')dnl                     # undivert 매크로
@

Include file start
foo                                        # undivert 는 매크로 확장이 안된다.
Include file end

만약에 파일이 존재하지 않거나, 읽을 수 없거나, 디렉토리 이거나 할 경우는 stderr 로 오류 메시지가 출력되고 확장 결과는 void 가 됩니다. 하지만 include 매크로와 달리 종료 상태 값은 0 이 됩니다.

$ m4 <<\@
111111111
undivert(`not_exist.m4')dnl
222222222
@

111111111
m4:stdin:2: cannot undivert `not_exist.m4': No such file or directory
222222222

$ echo $?        # 종료 상태 값은 0 이된다.
0

divnum

divnum built-in 매크로는 현재 divert 영역 번호를 반환합니다.

$ m4 <<\@
Initial divnum
divert(1)dnl
Diversion one: divnum
divert(2)dnl
Diversion two: divnum
divert(3)dnl
Diversion three: divnum
@

Initial 0
Diversion one: 1
Diversion two: 2
Diversion three: 3

cleardivert 사용자 정의 매크로

cleardivert
cleardivert ( Number . . . )

divert 영역을 삭제할 때 다음과 같이 매크로를 정의해 사용하면 편리합니다. cleardivert 는 전체 divert 영역을 삭제하고 Number 인수를 추가하면 해당 영역만 삭제됩니다.

define(`cleardivert',
    `pushdef(`tmp', divnum)divert(-1)ifelse($#, 0,
        `undivert`'', `undivert($@)')divert(tmp)popdef(`tmp')')