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 |