M_attr(3f) - [M_attr::INTRO] control text attributes on terminals
(LICENSE:MIT)
  use M_attr, only : attr, attr_mode, attr_update
  use M_attr, only : alert ! generate standard messages
M_attr(3f) is a Fortran module that uses common ANSI escape sequences
to control terminal text attributes.
     use M_attr
     write(*,*)attr('<red>Red Text!</red> <green>Green Text!</green>')
     end
It is designed to use three simple procedures to
 + Specify attributes using simple HTML-like syntax
 + allow the sequences to be suppressed when desired
 + permit the  user program to completely customize the keywords.
   The user can add, delete and replace the sequences associated with
   a keyword without changing the code.
One advantage of the approach of using formatting directives which
are replaced with in-band escape sequences is that it is easy to turn
off when running batch.
Another important capability is that programs can be run in "raw" mode
and create a simple text file with the formatting directives in it
that can then be read back in by a simple filter program that strips
it back to plain text( see app/plain.f90), or displays it to a screen
in color(see app/light.f90) or perhaps converts it to another format.
So this approach makes it trivial to read specially-formatted data
from a file like a message catalog (perhaps with various versions in
different languages) and colorize it or display it as plain text
By making each line self-contained (by default) lines can be filtered
by external utilities and still display correctly.
Via git(1):
    git clone https://github.com/urbanjost/M_attr.git
    cd M_attr/src
    # change Makefile if not using one of the listed compilers
    make clean; make gfortran    # for gfortran
    make clean; make ifort       # for ifort
    make clean; make nvfortran   # for nvfortran
This will compile the M_attr module and example programs.
Alternatively, via fpm (see https://github.com/fortran-lang/fpm):
     git clone https://github.com/urbanjost/M_attr.git
or just list it as a dependency in your fpm.toml project file.
     [dependencies]
     M_attr = { git = "https://github.com/urbanjost/M_attr.git" }
o colors are not nestable. o keywords are case-sensitive, o ANSI escape sequences are not universally supported by all terminal emulators; and normally should be suppressed when not going to a tty device. Therefore, you should use M_system::system_istty(3f) or the common Fortran extension ISATTY() to set the default to “plain” instead of “color” when the output file is not a conforming terminal. On basic MSWindows console windows, it is best to use Windows 10+ and/or the Linux mode; you may have to enable ANSI escape sequence mode on MSWindows. It does work as-is with CygWin and MinGW and Putty windows and mintty(1) as tested.
Sample program
 program demo_M_attr
 use M_attr, only : attr, attr_mode, attr_update, alert
 implicit none
 character(len=256) :: line
 character(len=*),parameter :: f='( &
  &"   <bo><w><G> GREAT: </G></w>&
  &The new value <Y><b>",f8.4,1x,"</b></Y> is in range"&
  &)'
 real :: value
    write(*,'(a)')&
    &attr('   <r><W><bo> ERROR: </W>red text on a white background</y>')
    value=3.4567
    write(line,fmt=f) value
    write(*,'(a)')attr(trim(line))
    ! write same string as plain text
    write(*,*)
    call attr_mode(manner='plain')
    write(*,'(a)')attr(trim(line))
    call attr_mode(manner='color')
    ! use pre-defined or user defined strings
    write(*,*)
    write(*,'(a)')attr('<ERROR> Woe is nigh.')
    write(*,'(a)')attr('<WARNING> The night is young.')
    write(*,'(a)')attr('<INFO> It is Monday')
    call alert('<ERROR>', 'Woe is nigh.')
    call alert('<WARNING>', 'The night is young.')
    call alert('<INFO>', 'It is Monday')
    ! create a custom mnemonic
    call attr_update('MYERROR',attr(&
    ' <R><e> E<w>-<e>R<w>-<e>R<w>-<e>O<w>-<e>R: </e></R></bo>'&
    ))
    write(*,*)
    write(*,'(a)')attr('<MYERROR> my custom message style')
 end program demo_M_attr
John S. Urban, 2021
MIT
attr(3f), attr_mode(3f), attr_update(3f)
Related information:
 terminfo(3c), termlib(3c), tput(1), reset(1), clear(1),
 console_codes(4), ECMA-48,
 https://en.wikipedia.org/wiki/ANSI_escape_code
| Type | Visibility | Attributes | Name | Initial | |||
|---|---|---|---|---|---|---|---|
| character(len=*), | public, | parameter | :: | bg_black | = | CODE_START//BG//BLACK//CODE_END | |
| character(len=*), | public, | parameter | :: | bg_blue | = | CODE_START//BG//BLUE//CODE_END | |
| character(len=*), | public, | parameter | :: | bg_cyan | = | CODE_START//BG//CYAN//CODE_END | |
| character(len=*), | public, | parameter | :: | bg_default | = | CODE_START//BG//DEFAULT//CODE_END | |
| character(len=*), | public, | parameter | :: | bg_ebony | = | CODE_START//BG//BLACK//CODE_END | |
| character(len=*), | public, | parameter | :: | bg_green | = | CODE_START//BG//GREEN//CODE_END | |
| character(len=*), | public, | parameter | :: | bg_magenta | = | CODE_START//BG//MAGENTA//CODE_END | |
| character(len=*), | public, | parameter | :: | bg_red | = | CODE_START//BG//RED//CODE_END | |
| character(len=*), | public, | parameter | :: | bg_white | = | CODE_START//BG//WHITE//CODE_END | |
| character(len=*), | public, | parameter | :: | bg_yellow | = | CODE_START//BG//YELLOW//CODE_END | |
| character(len=*), | public, | parameter | :: | bold | = | CODE_START//ON//AT_BOLD//CODE_END | |
| character(len=*), | public, | parameter | :: | clear | = | HOME_DISPLAY//CLEAR_DISPLAY | |
| character(len=*), | public, | parameter | :: | fg_black | = | CODE_START//FG//BLACK//CODE_END | |
| character(len=*), | public, | parameter | :: | fg_blue | = | CODE_START//FG//BLUE//CODE_END | |
| character(len=*), | public, | parameter | :: | fg_cyan | = | CODE_START//FG//CYAN//CODE_END | |
| character(len=*), | public, | parameter | :: | fg_default | = | CODE_START//FG//DEFAULT//CODE_END | |
| character(len=*), | public, | parameter | :: | fg_ebony | = | CODE_START//FG//BLACK//CODE_END | |
| character(len=*), | public, | parameter | :: | fg_green | = | CODE_START//FG//GREEN//CODE_END | |
| character(len=*), | public, | parameter | :: | fg_magenta | = | CODE_START//FG//MAGENTA//CODE_END | |
| character(len=*), | public, | parameter | :: | fg_red | = | CODE_START//FG//RED//CODE_END | |
| character(len=*), | public, | parameter | :: | fg_white | = | CODE_START//FG//WHITE//CODE_END | |
| character(len=*), | public, | parameter | :: | fg_yellow | = | CODE_START//FG//YELLOW//CODE_END | |
| character(len=*), | public, | parameter | :: | inverse | = | CODE_START//ON//AT_INVERSE//CODE_END | |
| character(len=*), | public, | parameter | :: | italic | = | CODE_START//ON//AT_ITALIC//CODE_END | |
| character(len=*), | public, | parameter | :: | reset | = | CODE_RESET | |
| character(len=*), | public, | parameter | :: | unbold | = | CODE_START//'22'//CODE_END | |
| character(len=*), | public, | parameter | :: | underline | = | CODE_START//ON//AT_UNDERLINE//CODE_END | |
| character(len=*), | public, | parameter | :: | uninverse | = | CODE_START//OFF//AT_INVERSE//CODE_END | |
| character(len=*), | public, | parameter | :: | unitalic | = | CODE_START//OFF//AT_ITALIC//CODE_END | |
| character(len=*), | public, | parameter | :: | ununderline | = | CODE_START//OFF//AT_UNDERLINE//CODE_END | 
!>
alert(3f) - [M_attr] print messages using a standard format including
time and program name
(LICENSE:MIT)
 subroutine alert(message,&
 g0,g1,g2,g3,g4,g5,g6,g7,g8,g9,ga,gb,gc,gd,ge,gf,gg,gh,gi,gj)
    character(len=*),intent(in),optional :: type
    character(len=*),intent(in),optional :: message
    class(*),intent(in),optional :: g0,g1,g2,g3,g4,g5,g6,g7,g8,g9, &
                                  & ga,gb,gc,gd,ge,gf,gg,gh,gi,gj
Display a message prefixed with a timestamp and the name
of the calling program when the TYPE is specified as any
of 'error','warn', or 'info'.
It also allows the keywords
<ARG0>,<TZ>,<YE>,<MO>,<DA>,<HR>,<MI>,<SE>,<MS> to be used in the
message (which is passed to ATTR(3f)).
Note that time stamp keywords will only be updated when using ALERT(3f)
and will only be displayed in color mode!
TYPE     if present and one of 'warn','message','info', or 'debug'
         a predefined message is written to stderr of the form
          : <HR>:<MI>:<SE>.<MS> : (<ARG0>) : TYPE -> message
MESSAGE  the user-supplied message to display via a call to ATTR(3f)
g[0-9a-j]   optional values to print after the message. May
            be of type INTEGER, LOGICAL, REAL, DOUBLEPRECISION,
            COMPLEX, or CHARACTER.
if no parameters are supplied the macros are updated but no output
is generated.
Sample program
 program demo_alert
 use M_attr, only : alert, attr, attr_mode
 implicit none
 real X
  call attr_mode(manner='plain')
  call attr_mode(manner='color')
  call alert("error",&
            "Say you didn't!")
  call alert("warn", &
            "I wouldn't if I were you, Will Robinson.")
  call alert("info", &
            "I fixed that for you, but it was a bad idea.")
  call alert("debug", &
            "Who knows what is happening now?.")
  call alert("???    ",  "not today you don't")
  ! call to just update the macros
  call alert()
  ! conventional call to ATTR(3f) using the ALERT(3f)-defined macros
  write(*,*)attr(&
          '<bo>The year was <g><YE></g>, the month was <g><MO></g>')
  ! optional arguments
  X=211.3
  call alert('error',&
          'allowed range of X is 0 <lt> X <lt> 100, X=<r>',X)
  ! up to twenty values are allowed of intrinsic type
  call alert('info','values are<g>',10,234.567,&
          cmplx(11.0,22.0),123.456d0,'</g>today')
 end program demo_alert
Results:
 00:38:30: (prg) : error    -> Say you didn't!
 00:38:30: (prg) : warning  -> I wouldn't if I were you, ...
                               Will Robinson.
 00:38:30: (prg) : info     -> I fixed that for you,  ...
                               but it was a bad idea.
 00:38:30: (prg) : debug    -> Who knows what is happening now?. ...
 00:38:30: (prg) : ???      -> not today you don't
 00:38:30: (prg) : error    -> allowed range of X is 0  X  100, ...
                               X= 211.300003
 00:38:30: (prg) : info     -> values are 10 234.567001 ...
                               (11.0000000,22.0000000) ...
                               123.45600000000000 today
John S. Urban, 2021
MIT
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| character(len=*), | intent(in), | optional | :: | type | ||
| character(len=*), | intent(in), | optional | :: | message | ||
| class(*), | intent(in), | optional | :: | g0 | ||
| class(*), | intent(in), | optional | :: | g1 | ||
| class(*), | intent(in), | optional | :: | g2 | ||
| class(*), | intent(in), | optional | :: | g3 | ||
| class(*), | intent(in), | optional | :: | g4 | ||
| class(*), | intent(in), | optional | :: | g5 | ||
| class(*), | intent(in), | optional | :: | g6 | ||
| class(*), | intent(in), | optional | :: | g7 | ||
| class(*), | intent(in), | optional | :: | g8 | ||
| class(*), | intent(in), | optional | :: | g9 | ||
| class(*), | intent(in), | optional | :: | ga | ||
| class(*), | intent(in), | optional | :: | gb | ||
| class(*), | intent(in), | optional | :: | gc | ||
| class(*), | intent(in), | optional | :: | gd | ||
| class(*), | intent(in), | optional | :: | ge | ||
| class(*), | intent(in), | optional | :: | gf | ||
| class(*), | intent(in), | optional | :: | gg | ||
| class(*), | intent(in), | optional | :: | gh | ||
| class(*), | intent(in), | optional | :: | gi | ||
| class(*), | intent(in), | optional | :: | gj | 
attr(3f) - [M_attr] substitute escape sequences for HTML-like syntax
           in strings
(LICENSE:MIT)
  function attr(string,reset) result (expanded)
    ! scalar
    character(len=*),intent(in)  :: string
    logical,intent(in),optional  :: reset
    character(len=:),allocatable :: expanded
    ! or array
    character(len=*),intent(in)  :: string(:)
    logical,intent(in),optional  :: reset
    character(len=:),allocatable :: expanded(:)
    integer,intent(in),optional  :: chars
Use HTML-like syntax to add attributes to terminal output such as
color on devices that recognize ANSI escape sequences.
string        input string  of form
                "<attribute_name>string</attribute_name> ...".
               where the current attributes are color names,
               bold, italic, underline, ...
reset          By default, a sequence to clear all text attributes
               is sent at the end of each returned line if an escape
               character appears in the output string. This can be
               turned off by setting RESET to .false. .
               Note if turning off the reset attributes may be
               continued across lines, but if each line is not
               self-contained attributes may not display properly
               when filtered with commands such as grep(1).
chars          For arrays, a reset will be placed after the Nth
               displayable column count in order to make it easier
               to generate consistent right borders for non-default
               background colors for a text block.
primary default keywords
  colors:
    r,         red,       R,  RED
    g,         green,     G,  GREEN
    b,         blue,      B,  BLUE
    m,         magenta,   M,  MAGENTA
    c,         cyan,      C,  CYAN
    y,         yellow,    Y,  YELLOW
    e,         ebony,     E,  EBONY
    w,         white,     W,  WHITE
  attributes:
    it,        italic
    bo,        bold
    un,        underline
  basic control characters:
   nul
   bel  (0x07, ^G) beeps;
   bs   (0x08, ^H) backspaces one column (but not past the beginning of
                   the line);
   ht   (0x09, ^I) goes to the next tab stop or to the end of the line if
                   there is no earlier tab stop;
   lf   (0x0A, ^J),
   vt   (0x0B, ^K)
   ff   (0x0C, ^L) all give a linefeed, and if LF/NL (new-line mode) is
                   set also a carriage return
   cr   (0x0D, ^M) gives a carriage return;
   so   (0x0E, ^N) activates the G1 character set;
   si   (0x0F, ^O) activates the G0 character set;
   can  (0x18, ^X) and SUB (0x1A, ^Z) interrupt escape sequences;
   sub
   esc  (0x1B, ^[) starts an escape sequence;
   del  (0x7F) is ignored;
  other:
    clear
    default
    reset
    gt
    lt
    save,DECSC     Save  current state (cursor coordinates, attributes,
                   character sets pointed at by G0, G1).
    restore,DECRC  Restore state most recently saved by ESC 7.
    CSI            "Control Sequence Introducer"(0x9B) is equivalent to
                   "ESC [".
  dual-value (one for color, one for mono):
    write(*,*)attr('<ERROR>an error message')
    write(*,*)attr('<WARNING>a warning message')
    write(*,*)attr('<INFO>an informational message')
By default, if the color mnemonics (ie. the keywords) are uppercase
they change the background color. If lowercase, the foreground color.
When preceded by a "/" character the attribute is returned to the
default.
The "default" keyword is typically used explicitly when reset=.false,
and sets all text attributes to their initial defaults.
o colors are not nestable, keywords are case-sensitive,
o not all terminals obey the sequences. On Windows, it is best if
  you use Windows 10+ and/or the Linux mode; although it has worked
  with all CygWin and MinGW and Putty windows and mintty.
o you should use "<gt>" and "<lt>" instead of ">" and "<" in a string
  processed by attr(3f) instead of in any plain text output so that
  the raw mode will create correct input for the attr(3f) function
  if read back in.
Sample program
 program demo_attr
 use M_attr, only : attr, attr_mode, attr_update
    call printstuff('defaults')
    call attr_mode(manner='plain')
    call printstuff('plain:')
    call printstuff('raw')
    call attr_mode(manner='color')
    call printstuff('')
    write(*,'(a)') attr('TEST ADDING A CUSTOM SEQUENCE:')
    call attr_update('blink',char(27)//'[5m')
    call attr_update('/blink',char(27)//'[25m')
    write(*,'(a)') attr('<blink>Items for Friday</blink>')
 contains
 subroutine printstuff(label)
 character(len=*),intent(in)  :: label
 character(len=:),allocatable :: array(:)
   call attr_mode(manner=label)
   array=[character(len=60) ::    &
    'TEST MANNER='//label,                      &
    '<r>RED</r>,<g>GREEN</g>,<b>BLUE</b>',      &
    '<c>CYAN</c>,<m>MAGENTA</g>,<y>YELLOW</y>', &
    '<w>WHITE</w> and <e>EBONY</e>']
   write(*,'(a)') attr(array)
   write(*,'(a)') attr('Adding <bo>bold</bo>')
   write(*,'(a)') attr('<bo><r>RED</r>,<g>GREEN</g>,<b>BLUE</b></bo>')
   write(*,'(a)') attr('<bo><c>CYAN</c>,<m>MAGENTA</g>,<y>YELLOW</y></bo>')
   write(*,'(a)') attr('<bo><w>WHITE</w> and <e>EBONY</e></bo>')
   write(*,'(a)') attr('Adding <ul>underline</ul>')
   write(*,'(a)') attr(&
    &'<bo><ul><r>RED</r>,<g>GREEN</g>,<b>BLUE</b></ul></bo>')
   write(*,'(a)') attr(&
    &'<bo><ul><c>CYAN</c>,<m>MAGENTA</g>,<y>YELLOW</y></ul></bo>')
   write(*,'(a)') attr('<bo><ul><w>WHITE</w> and <e>EBONY</e></ul></bo>')
   write(*,'(a)') attr('Adding <ul>italic</ul>')
   write(*,'(a)') attr(&
    &'<bo><ul><it><r>RED</r>,<g>GREEN</g>,<b>BLUE</b></it></ul></bo>')
   write(*,'(a)') attr(&
    &'<bo><ul><it><c>CYAN</c>,<m>MAGENTA</g>,<y>YELLOW</it></y></ul></bo>')
   write(*,'(a)') attr('<bo><ul><it><w>WHITE</w> and <e>EBONY</e></ul></bo>')
   write(*,'(a)') attr('Adding <in>inverse</in>')
   write(*,'(a)') attr('<in><bo><ul><it><r>RED</r>,<g>GREEN</g>,&
    &<b>BLUE</b></it></ul></bo></in>')
   write(*,'(a)') attr('<in><bo><ul><it><c>CYAN</c>,<m>MAGENTA</g>,&
    &<y>YELLOW</it></y></ul></bo></in>')
   write(*,'(a)') attr(&
    &'<in><bo><ul><it><w>WHITE</w> and <e>EBONY</e></ul></bo></in>')
 end subroutine printstuff
 end program demo_attr
John S. Urban, 2021
MIT
attr_mode(3f), attr_update(3f)
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| character(len=*), | intent(in) | :: | string | |||
| logical, | intent(in), | optional | :: | reset | 
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| character(len=*), | intent(in) | :: | strings(:) | |||
| logical, | intent(in), | optional | :: | reset | ||
| integer, | intent(in), | optional | :: | chars | 
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| character(len=*), | intent(in) | :: | string | |||
| logical, | intent(in), | optional | :: | reset | ||
| integer, | intent(in) | :: | chars | 
!>
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| character(len=*), | intent(in), | optional | :: | type | ||
| character(len=*), | intent(in), | optional | :: | message | ||
| class(*), | intent(in), | optional | :: | g0 | ||
| class(*), | intent(in), | optional | :: | g1 | ||
| class(*), | intent(in), | optional | :: | g2 | ||
| class(*), | intent(in), | optional | :: | g3 | ||
| class(*), | intent(in), | optional | :: | g4 | ||
| class(*), | intent(in), | optional | :: | g5 | ||
| class(*), | intent(in), | optional | :: | g6 | ||
| class(*), | intent(in), | optional | :: | g7 | ||
| class(*), | intent(in), | optional | :: | g8 | ||
| class(*), | intent(in), | optional | :: | g9 | ||
| class(*), | intent(in), | optional | :: | ga | ||
| class(*), | intent(in), | optional | :: | gb | ||
| class(*), | intent(in), | optional | :: | gc | ||
| class(*), | intent(in), | optional | :: | gd | ||
| class(*), | intent(in), | optional | :: | ge | ||
| class(*), | intent(in), | optional | :: | gf | ||
| class(*), | intent(in), | optional | :: | gg | ||
| class(*), | intent(in), | optional | :: | gh | ||
| class(*), | intent(in), | optional | :: | gi | ||
| class(*), | intent(in), | optional | :: | gj | 
!>
| Type | Intent | Optional | Attributes | Name | ||
|---|---|---|---|---|---|---|
| character(len=*), | intent(in) | :: | key | |||
| character(len=*), | intent(in), | optional | :: | valin | ||
| character(len=*), | intent(in), | optional | :: | mono_valin |