mosquitto_sub now supports extra format specifiers.
These are for field width and precision for some parameters.
This commit is contained in:
parent
18e79eac22
commit
bab8cc2a6b
@ -56,6 +56,8 @@ Clients:
|
||||
of received messages to be printed.
|
||||
- mosquitto_sub %j and %J timestamps are now in a ISO 8601 compatible format.
|
||||
- mosquitto_pub now sends 0 length files without an error when using `-f`.
|
||||
- mosquitto_sub now supports extra format specifiers for field width and
|
||||
precision for some parameters.
|
||||
|
||||
|
||||
1.6.9 - 20200227
|
||||
|
@ -55,6 +55,45 @@ static int check_format(const char *str)
|
||||
fprintf(stderr, "Error: Incomplete format specifier.\n");
|
||||
return 1;
|
||||
}else{
|
||||
if(str[i+1] == '0' || str[i+1] == '-'){
|
||||
/* Flag characters */
|
||||
i++;
|
||||
if(i == len-1){
|
||||
// error
|
||||
fprintf(stderr, "Error: Incomplete format specifier.\n");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
/* Field width */
|
||||
while(str[i+1] >= '0' && str[i+1] <= '9'){
|
||||
i++;
|
||||
if(i == len-1){
|
||||
// error
|
||||
fprintf(stderr, "Error: Incomplete format specifier.\n");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
|
||||
if(str[i+1] == '.'){
|
||||
/* Precision specifier */
|
||||
i++;
|
||||
if(i == len-1){
|
||||
// error
|
||||
fprintf(stderr, "Error: Incomplete format specifier.\n");
|
||||
return 1;
|
||||
}
|
||||
/* Precision */
|
||||
while(str[i+1] >= '0' && str[i+1] <= '9'){
|
||||
i++;
|
||||
if(i == len-1){
|
||||
// error
|
||||
fprintf(stderr, "Error: Incomplete format specifier.\n");
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(str[i+1] == '%'){
|
||||
// Print %, ignore
|
||||
}else if(str[i+1] == 'A'){
|
||||
|
@ -84,9 +84,30 @@ static int get_time(struct tm **ti, long *ns)
|
||||
}
|
||||
|
||||
|
||||
static void write_payload(const unsigned char *payload, int payloadlen, int hex)
|
||||
static void write_payload(const unsigned char *payload, int payloadlen, int hex, char align, char pad, int field_width, int precision)
|
||||
{
|
||||
int i;
|
||||
int padlen;
|
||||
|
||||
if(field_width > 0){
|
||||
if(payloadlen > field_width){
|
||||
payloadlen = field_width;
|
||||
}
|
||||
if(hex > 0){
|
||||
payloadlen /= 2;
|
||||
padlen = field_width - payloadlen*2;
|
||||
}else{
|
||||
padlen = field_width - payloadlen;
|
||||
}
|
||||
}else{
|
||||
padlen = field_width - payloadlen;
|
||||
}
|
||||
|
||||
if(align != '-'){
|
||||
for(i=0; i<padlen; i++){
|
||||
putchar(pad);
|
||||
}
|
||||
}
|
||||
|
||||
if(hex == 0){
|
||||
(void)fwrite(payload, 1, payloadlen, stdout);
|
||||
@ -99,6 +120,10 @@ static void write_payload(const unsigned char *payload, int payloadlen, int hex)
|
||||
fprintf(stdout, "%02X", payload[i]);
|
||||
}
|
||||
}
|
||||
|
||||
if(align == '-'){
|
||||
printf("%*s", padlen, "");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -325,7 +350,7 @@ static int json_print(const struct mosquitto_message *message, const mosquitto_p
|
||||
fputs("\"}", stdout);
|
||||
}else{
|
||||
fputs("\"payload\":", stdout);
|
||||
write_payload(message->payload, message->payloadlen, 0);
|
||||
write_payload(message->payload, message->payloadlen, 0, 0, 0, 0);
|
||||
fputs("}", stdout);
|
||||
}
|
||||
|
||||
@ -334,13 +359,64 @@ static int json_print(const struct mosquitto_message *message, const mosquitto_p
|
||||
}
|
||||
|
||||
|
||||
static void formatted_print(const struct mosq_config *lcfg, const struct mosquitto_message *message, const mosquitto_property *properties)
|
||||
static void formatted_print_blank(char pad, int field_width)
|
||||
{
|
||||
int len;
|
||||
int i;
|
||||
for(i=0; i<field_width; i++){
|
||||
putchar(pad);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void formatted_print_int(int value, char align, char pad, int field_width)
|
||||
{
|
||||
if(field_width == 0){
|
||||
printf("%d", value);
|
||||
}else{
|
||||
if(align == '-'){
|
||||
printf("%-*d", field_width, value);
|
||||
}else{
|
||||
if(pad == '0'){
|
||||
printf("%0*d", field_width, value);
|
||||
}else{
|
||||
printf("%*d", field_width, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void formatted_print_str(const char *value, char align, int field_width, int precision)
|
||||
{
|
||||
if(field_width == 0 && precision == -1){
|
||||
fputs(value, stdout);
|
||||
}else{
|
||||
if(precision == -1){
|
||||
if(align == '-'){
|
||||
printf("%-*s", field_width, value);
|
||||
}else{
|
||||
printf("%*s", field_width, value);
|
||||
}
|
||||
}else if(field_width == 0){
|
||||
if(align == '-'){
|
||||
printf("%-.*s", precision, value);
|
||||
}else{
|
||||
printf("%.*s", precision, value);
|
||||
}
|
||||
}else{
|
||||
if(align == '-'){
|
||||
printf("%-*.*s", field_width, precision, value);
|
||||
}else{
|
||||
printf("%*.*s", field_width, precision, value);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
static void formatted_print_percent(const struct mosq_config *lcfg, const struct mosquitto_message *message, const mosquitto_property *properties, char format, char align, char pad, int field_width, int precision)
|
||||
{
|
||||
struct tm *ti = NULL;
|
||||
long ns;
|
||||
char strf[3];
|
||||
char buf[100];
|
||||
int rc;
|
||||
uint8_t i8value;
|
||||
@ -349,27 +425,26 @@ static void formatted_print(const struct mosq_config *lcfg, const struct mosquit
|
||||
char *binvalue, *strname, *strvalue;
|
||||
const mosquitto_property *prop;
|
||||
|
||||
len = strlen(lcfg->format);
|
||||
|
||||
for(i=0; i<len; i++){
|
||||
if(lcfg->format[i] == '%'){
|
||||
if(i < len-1){
|
||||
i++;
|
||||
switch(lcfg->format[i]){
|
||||
switch(format){
|
||||
case '%':
|
||||
fputc('%', stdout);
|
||||
break;
|
||||
|
||||
case 'A':
|
||||
if(mosquitto_property_read_int16(properties, MQTT_PROP_TOPIC_ALIAS, &i16value, false)){
|
||||
printf("%d", i16value);
|
||||
formatted_print_int(i16value, align, pad, field_width);
|
||||
}else{
|
||||
formatted_print_blank(pad, field_width);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'C':
|
||||
if(mosquitto_property_read_string(properties, MQTT_PROP_CONTENT_TYPE, &strvalue, false)){
|
||||
printf("%s", strvalue);
|
||||
formatted_print_str(strvalue, align, field_width, precision);
|
||||
free(strvalue);
|
||||
}else{
|
||||
formatted_print_blank(' ', field_width);
|
||||
}
|
||||
break;
|
||||
|
||||
@ -382,13 +457,17 @@ static void formatted_print(const struct mosq_config *lcfg, const struct mosquit
|
||||
|
||||
case 'E':
|
||||
if(mosquitto_property_read_int32(properties, MQTT_PROP_MESSAGE_EXPIRY_INTERVAL, &i32value, false)){
|
||||
printf("%d", i32value);
|
||||
formatted_print_int(i32value, align, pad, field_width);
|
||||
}else{
|
||||
formatted_print_blank(pad, field_width);
|
||||
}
|
||||
break;
|
||||
|
||||
case 'F':
|
||||
if(mosquitto_property_read_byte(properties, MQTT_PROP_PAYLOAD_FORMAT_INDICATOR, &i8value, false)){
|
||||
printf("%d", i8value);
|
||||
formatted_print_int(i8value, align, pad, field_width);
|
||||
}else{
|
||||
formatted_print_blank(pad, field_width);
|
||||
}
|
||||
break;
|
||||
|
||||
@ -400,7 +479,9 @@ static void formatted_print(const struct mosq_config *lcfg, const struct mosquit
|
||||
}
|
||||
}
|
||||
if(strftime(buf, 100, "%FT%T%z", ti) != 0){
|
||||
fputs(buf, stdout);
|
||||
formatted_print_str(buf, align, field_width, precision);
|
||||
}else{
|
||||
formatted_print_blank(' ', field_width);
|
||||
}
|
||||
break;
|
||||
|
||||
@ -435,11 +516,11 @@ static void formatted_print(const struct mosq_config *lcfg, const struct mosquit
|
||||
break;
|
||||
|
||||
case 'l':
|
||||
printf("%d", message->payloadlen);
|
||||
formatted_print_int(message->payloadlen, align, pad, field_width);
|
||||
break;
|
||||
|
||||
case 'm':
|
||||
printf("%d", message->mid);
|
||||
formatted_print_int(message->mid, align, pad, field_width);
|
||||
break;
|
||||
|
||||
case 'P':
|
||||
@ -463,7 +544,7 @@ static void formatted_print(const struct mosq_config *lcfg, const struct mosquit
|
||||
break;
|
||||
|
||||
case 'p':
|
||||
write_payload(message->payload, message->payloadlen, 0);
|
||||
write_payload(message->payload, message->payloadlen, 0, align, pad, field_width, precision);
|
||||
break;
|
||||
|
||||
case 'q':
|
||||
@ -472,7 +553,7 @@ static void formatted_print(const struct mosq_config *lcfg, const struct mosquit
|
||||
|
||||
case 'R':
|
||||
if(mosquitto_property_read_string(properties, MQTT_PROP_RESPONSE_TOPIC, &strvalue, false)){
|
||||
printf("%s", strvalue);
|
||||
formatted_print_str(strvalue, align, field_width, precision);
|
||||
free(strvalue);
|
||||
}
|
||||
break;
|
||||
@ -487,12 +568,14 @@ static void formatted_print(const struct mosq_config *lcfg, const struct mosquit
|
||||
|
||||
case 'S':
|
||||
if(mosquitto_property_read_varint(properties, MQTT_PROP_SUBSCRIPTION_IDENTIFIER, &i32value, false)){
|
||||
printf("%d", i32value);
|
||||
formatted_print_int(i32value, align, pad, field_width);
|
||||
}else{
|
||||
formatted_print_blank(pad, field_width);
|
||||
}
|
||||
break;
|
||||
|
||||
case 't':
|
||||
fputs(message->topic, stdout);
|
||||
formatted_print_str(message->topic, align, field_width, precision);
|
||||
break;
|
||||
|
||||
case 'U':
|
||||
@ -508,13 +591,75 @@ static void formatted_print(const struct mosq_config *lcfg, const struct mosquit
|
||||
break;
|
||||
|
||||
case 'x':
|
||||
write_payload(message->payload, message->payloadlen, 1);
|
||||
write_payload(message->payload, message->payloadlen, 1, align, pad, field_width, precision);
|
||||
break;
|
||||
|
||||
case 'X':
|
||||
write_payload(message->payload, message->payloadlen, 2);
|
||||
write_payload(message->payload, message->payloadlen, 2, align, pad, field_width, precision);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void formatted_print(const struct mosq_config *lcfg, const struct mosquitto_message *message, const mosquitto_property *properties)
|
||||
{
|
||||
int len;
|
||||
int i;
|
||||
struct tm *ti = NULL;
|
||||
long ns;
|
||||
char strf[3];
|
||||
char buf[100];
|
||||
char align, pad;
|
||||
int field_width, precision;
|
||||
|
||||
len = strlen(lcfg->format);
|
||||
|
||||
for(i=0; i<len; i++){
|
||||
if(lcfg->format[i] == '%'){
|
||||
align = 0;
|
||||
pad = ' ';
|
||||
field_width = 0;
|
||||
precision = -1;
|
||||
if(i < len-1){
|
||||
i++;
|
||||
/* Optional alignment */
|
||||
if(lcfg->format[i] == '-'){
|
||||
align = lcfg->format[i];
|
||||
if(i < len-1){
|
||||
i++;
|
||||
}
|
||||
}
|
||||
/* "%-040p" is allowed by this combination of checks, but isn't
|
||||
* a valid format specifier, the '0' will be ignored. */
|
||||
/* Optional zero padding */
|
||||
if(lcfg->format[i] == '0'){
|
||||
pad = '0';
|
||||
if(i < len-1){
|
||||
i++;
|
||||
}
|
||||
}
|
||||
/* Optional field width */
|
||||
while(i < len-1 && lcfg->format[i] >= '0' && lcfg->format[i] <= '9'){
|
||||
field_width *= 10;
|
||||
field_width += lcfg->format[i]-'0';
|
||||
i++;
|
||||
}
|
||||
/* Optional precision */
|
||||
if(lcfg->format[i] == '.'){
|
||||
if(i < len-1){
|
||||
i++;
|
||||
precision = 0;
|
||||
while(i < len-1 && lcfg->format[i] >= '0' && lcfg->format[i] <= '9'){
|
||||
precision *= 10;
|
||||
precision += lcfg->format[i]-'0';
|
||||
i++;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(i < len){
|
||||
formatted_print_percent(lcfg, message, properties, lcfg->format[i], align, pad, field_width, precision);
|
||||
}
|
||||
}
|
||||
}else if(lcfg->format[i] == '@'){
|
||||
if(i < len-1){
|
||||
@ -616,7 +761,7 @@ void print_message(struct mosq_config *cfg, const struct mosquitto_message *mess
|
||||
}else if(cfg->verbose){
|
||||
if(message->payloadlen){
|
||||
printf("%s ", message->topic);
|
||||
write_payload(message->payload, message->payloadlen, false);
|
||||
write_payload(message->payload, message->payloadlen, false, 0, 0, 0, 0);
|
||||
if(cfg->eol){
|
||||
printf("\n");
|
||||
}
|
||||
@ -628,7 +773,7 @@ void print_message(struct mosq_config *cfg, const struct mosquitto_message *mess
|
||||
fflush(stdout);
|
||||
}else{
|
||||
if(message->payloadlen){
|
||||
write_payload(message->payload, message->payloadlen, false);
|
||||
write_payload(message->payload, message->payloadlen, false, 0, 0, 0, 0);
|
||||
if(cfg->eol){
|
||||
printf("\n");
|
||||
}
|
||||
|
59
client/sub_test_fixed_width
Executable file
59
client/sub_test_fixed_width
Executable file
@ -0,0 +1,59 @@
|
||||
LD_LIBRARY_PATH=../lib ./mosquitto_sub \
|
||||
-h test.mosquitto.org \
|
||||
--retained-only \
|
||||
--remove-retained \
|
||||
-t FW/# \
|
||||
-W 1>/dev/null 2>/dev/null
|
||||
|
||||
LD_LIBRARY_PATH=../lib ./mosquitto_pub \
|
||||
-h test.mosquitto.org \
|
||||
-D publish content-type "application/json" \
|
||||
-D publish message-expiry-interval 360000 \
|
||||
-D publish payload-format-indicator 1 \
|
||||
-D publish response-topic response-topic \
|
||||
-m ABCDEFGHIJKLMNOPQRSTUVWXYZ \
|
||||
-q 2 \
|
||||
-r \
|
||||
-t FW/truncate \
|
||||
-V 5
|
||||
|
||||
LD_LIBRARY_PATH=../lib ./mosquitto_pub \
|
||||
-h test.mosquitto.org \
|
||||
-D publish content-type "null" \
|
||||
-D publish message-expiry-interval 3600 \
|
||||
-D publish payload-format-indicator 1 \
|
||||
-D publish response-topic r-t \
|
||||
-m Off \
|
||||
-q 2 \
|
||||
-r \
|
||||
-t FW/expire \
|
||||
-V 5
|
||||
|
||||
LD_LIBRARY_PATH=../lib ./mosquitto_pub \
|
||||
-h test.mosquitto.org \
|
||||
-D publish payload-format-indicator 1 \
|
||||
-D publish response-topic rt \
|
||||
-m Offline \
|
||||
-q 2 \
|
||||
-r \
|
||||
-t FW/1 \
|
||||
-V 5
|
||||
|
||||
LD_LIBRARY_PATH=../lib ./mosquitto_sub \
|
||||
-h test.mosquitto.org \
|
||||
-C 3 \
|
||||
-F "| %10t | %-10t | %7x | %-7x | %08x | %7X | %-7X | %08X | %8p | %-8p | %3m | %-3m | %03m | %3l | %-3l | %03l | %2F | %-2F | %02F | %5C | %-5C | %5E | %-5E | %05E | %5A | %5R | %-5R |" \
|
||||
-q 2 \
|
||||
-t 'FW/#' \
|
||||
-V 5
|
||||
|
||||
echo
|
||||
|
||||
LD_LIBRARY_PATH=../lib ./mosquitto_sub \
|
||||
-h test.mosquitto.org \
|
||||
-C 3 \
|
||||
-F "| %10.10t | %.5t | %-10.10t | %7x | %-7x | %08x | %7X | %-7X | %08X | %8p | %-8p | %3m | %-3m | %03m | %3l | %-3l | %03l | %2F | %-2F | %02F | %5C | %-5C | %5E | %-5E | %05E | %5.5A | %5.5R | %-5.5R |" \
|
||||
-q 2 \
|
||||
-t 'FW/#' \
|
||||
-V 5
|
||||
|
32
man/Makefile
32
man/Makefile
@ -50,32 +50,32 @@ uninstall :
|
||||
-rm -f "${DESTDIR}${mandir}/man7/mosquitto-tls.7"
|
||||
-rm -f "${DESTDIR}${mandir}/man3/libmosquitto.3"
|
||||
|
||||
mosquitto.8 : mosquitto.8.xml
|
||||
$(XSLTPROC) $^
|
||||
mosquitto.8 : mosquitto.8.xml manpage.xsl
|
||||
$(XSLTPROC) $<
|
||||
|
||||
mosquitto.conf.5 : mosquitto.conf.5.xml manpage.xsl
|
||||
$(XSLTPROC) $<
|
||||
|
||||
mosquitto_passwd.1 : mosquitto_passwd.1.xml
|
||||
$(XSLTPROC) $^
|
||||
mosquitto_passwd.1 : mosquitto_passwd.1.xml manpage.xsl
|
||||
$(XSLTPROC) $<
|
||||
|
||||
mosquitto_pub.1 : mosquitto_pub.1.xml
|
||||
$(XSLTPROC) $^
|
||||
mosquitto_pub.1 : mosquitto_pub.1.xml manpage.xsl
|
||||
$(XSLTPROC) $<
|
||||
|
||||
mosquitto_sub.1 : mosquitto_sub.1.xml
|
||||
$(XSLTPROC) $^
|
||||
mosquitto_sub.1 : mosquitto_sub.1.xml manpage.xsl
|
||||
$(XSLTPROC) $<
|
||||
|
||||
mosquitto_rr.1 : mosquitto_rr.1.xml
|
||||
$(XSLTPROC) $^
|
||||
mosquitto_rr.1 : mosquitto_rr.1.xml manpage.xsl
|
||||
$(XSLTPROC) $<
|
||||
|
||||
mqtt.7 : mqtt.7.xml
|
||||
$(XSLTPROC) $^
|
||||
mqtt.7 : mqtt.7.xml manpage.xsl
|
||||
$(XSLTPROC) $<
|
||||
|
||||
mosquitto-tls.7 : mosquitto-tls.7.xml
|
||||
$(XSLTPROC) $^
|
||||
mosquitto-tls.7 : mosquitto-tls.7.xml manpage.xsl
|
||||
$(XSLTPROC) $<
|
||||
|
||||
libmosquitto.3 : libmosquitto.3.xml
|
||||
$(XSLTPROC) $^
|
||||
libmosquitto.3 : libmosquitto.3.xml manpage.xsl
|
||||
$(XSLTPROC) $<
|
||||
|
||||
html : *.xml
|
||||
set -e; for m in *.xml; \
|
||||
|
@ -790,7 +790,7 @@ mosquitto_sub -t 'bbc/#' -T bbc/bbc1 --remove-retained</programlisting>
|
||||
</refsect1>
|
||||
|
||||
<refsect1 id='outputformat'>
|
||||
<title>Output format</title>
|
||||
<title>Output Format</title>
|
||||
<para>There are three ways of formatting the output from mosquitto_sub.
|
||||
In all cases a new-line character is appended for each message
|
||||
received unless the <option>-N</option> argument is passed to
|
||||
@ -824,6 +824,58 @@ mosquitto_sub -t 'bbc/#' -T bbc/bbc1 --remove-retained</programlisting>
|
||||
character is <option>\</option>, which is used to input some
|
||||
characters that would otherwise be difficult to enter.</para>
|
||||
|
||||
<refsect2>
|
||||
<title>Flag characters</title>
|
||||
<para>The parameters %A, %C, %E, %F, %I, %l, %m, %p, %R, %S, %t, %x, and %X can have optional flags immediately after the % character.</para>
|
||||
<variablelist>
|
||||
<varlistentry>
|
||||
<term><option>0</option></term>
|
||||
<listitem><para>The value should be zero padded.
|
||||
This applies to the parameters %A, %E, %F, %l, %m, %S, %X, and %x.
|
||||
It will be ignored for other parameters. If used with the
|
||||
<option>-</option> flag, the <option>0</option> flag will be
|
||||
ignored.</para></listitem>
|
||||
</varlistentry>
|
||||
|
||||
<varlistentry>
|
||||
<term><option>-</option></term>
|
||||
<listitem><para>The value will be left aligned to the field width,
|
||||
padded with blanks. The default is right alignment, with either 0
|
||||
or blank padding.</para></listitem>
|
||||
</varlistentry>
|
||||
</variablelist>
|
||||
</refsect2>
|
||||
|
||||
<refsect2>
|
||||
<title>Field width</title>
|
||||
<para>
|
||||
Some of the MQTT related parameters can be formatted with an
|
||||
option to set their field width in a similar way to regular
|
||||
printf style formats, i.e. this sets the minimum width when
|
||||
printing this parameter. This applies to the options %A, %C,
|
||||
%E, %F, %I, %l, %m, %p, %R, %S, %t, %x, %X.
|
||||
</para>
|
||||
<para>
|
||||
For example <option>%10t</option> would set the minimum topic
|
||||
field width to 10 characters.
|
||||
</para>
|
||||
</refsect2>
|
||||
|
||||
<refsect2>
|
||||
<title>Maximum width</title>
|
||||
<para>
|
||||
Some of the MQTT related parameters can be formatted with an
|
||||
option to set a maximum field width in a similar way to regular
|
||||
printf style formats. This applies to the options %C, %I, %R, %t.
|
||||
</para>
|
||||
<para>
|
||||
For example <option>%10.10t</option> would set the minimum topic
|
||||
field width to 10 characters, and the maximum topic width to
|
||||
10 characters, i.e. the field will always be exactly 10
|
||||
characters long.
|
||||
</para>
|
||||
</refsect2>
|
||||
|
||||
<refsect2>
|
||||
<title>MQTT related parameters</title>
|
||||
<itemizedlist mark="circle">
|
||||
|
Loading…
Reference in New Issue
Block a user