sub_client_output.c 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834
  1. /*
  2. Copyright (c) 2009-2020 Roger Light <roger@atchoo.org>
  3. All rights reserved. This program and the accompanying materials
  4. are made available under the terms of the Eclipse Public License 2.0
  5. and Eclipse Distribution License v1.0 which accompany this distribution.
  6. The Eclipse Public License is available at
  7. https://www.eclipse.org/legal/epl-2.0/
  8. and the Eclipse Distribution License is available at
  9. http://www.eclipse.org/org/documents/edl-v10.php.
  10. SPDX-License-Identifier: EPL-2.0 OR BSD-3-Clause
  11. Contributors:
  12. Roger Light - initial implementation and documentation.
  13. */
  14. #include "config.h"
  15. #ifdef WIN32
  16. /* For rand_s on Windows */
  17. # define _CRT_RAND_S
  18. # include <fcntl.h>
  19. # include <io.h>
  20. #endif
  21. #include <assert.h>
  22. #include <errno.h>
  23. #include <stdio.h>
  24. #include <stdlib.h>
  25. #include <string.h>
  26. #include <time.h>
  27. #ifndef WIN32
  28. #include <unistd.h>
  29. #else
  30. #include <process.h>
  31. #include <winsock2.h>
  32. #define snprintf sprintf_s
  33. #endif
  34. #ifdef WITH_CJSON
  35. # include <cjson/cJSON.h>
  36. #endif
  37. #ifdef __APPLE__
  38. # include <sys/time.h>
  39. #endif
  40. #include <mosquitto.h>
  41. #include <mqtt_protocol.h>
  42. #include "client_shared.h"
  43. #include "sub_client_output.h"
  44. extern struct mosq_config cfg;
  45. static int get_time(struct tm **ti, long *ns)
  46. {
  47. #ifdef WIN32
  48. SYSTEMTIME st;
  49. #elif defined(__APPLE__)
  50. struct timeval tv;
  51. #else
  52. struct timespec ts;
  53. #endif
  54. time_t s;
  55. #ifdef WIN32
  56. s = time(NULL);
  57. GetLocalTime(&st);
  58. *ns = st.wMilliseconds*1000000L;
  59. #elif defined(__APPLE__)
  60. gettimeofday(&tv, NULL);
  61. s = tv.tv_sec;
  62. *ns = tv.tv_usec*1000;
  63. #else
  64. if(clock_gettime(CLOCK_REALTIME, &ts) != 0){
  65. err_printf(&cfg, "Error obtaining system time.\n");
  66. return 1;
  67. }
  68. s = ts.tv_sec;
  69. *ns = ts.tv_nsec;
  70. #endif
  71. *ti = localtime(&s);
  72. if(!(*ti)){
  73. err_printf(&cfg, "Error obtaining system time.\n");
  74. return 1;
  75. }
  76. return 0;
  77. }
  78. static void write_payload(const unsigned char *payload, int payloadlen, int hex, char align, char pad, int field_width, int precision)
  79. {
  80. int i;
  81. int padlen;
  82. UNUSED(precision); /* FIXME - use or remove */
  83. if(field_width > 0){
  84. if(payloadlen > field_width){
  85. payloadlen = field_width;
  86. }
  87. if(hex > 0){
  88. payloadlen /= 2;
  89. padlen = field_width - payloadlen*2;
  90. }else{
  91. padlen = field_width - payloadlen;
  92. }
  93. }else{
  94. padlen = field_width - payloadlen;
  95. }
  96. if(align != '-'){
  97. for(i=0; i<padlen; i++){
  98. putchar(pad);
  99. }
  100. }
  101. if(hex == 0){
  102. (void)fwrite(payload, 1, (size_t )payloadlen, stdout);
  103. }else if(hex == 1){
  104. for(i=0; i<payloadlen; i++){
  105. fprintf(stdout, "%02x", payload[i]);
  106. }
  107. }else if(hex == 2){
  108. for(i=0; i<payloadlen; i++){
  109. fprintf(stdout, "%02X", payload[i]);
  110. }
  111. }
  112. if(align == '-'){
  113. printf("%*s", padlen, "");
  114. }
  115. }
  116. #ifndef WITH_CJSON
  117. static void write_json_payload(const char *payload, int payloadlen)
  118. {
  119. int i;
  120. for(i=0; i<payloadlen; i++){
  121. if(payload[i] == '"' || payload[i] == '\\' || (payload[i] >=0 && payload[i] < 32)){
  122. printf("\\u%04x", payload[i]);
  123. }else{
  124. fputc(payload[i], stdout);
  125. }
  126. }
  127. }
  128. #endif
  129. #ifdef WITH_CJSON
  130. static int json_print_properties(cJSON *root, const mosquitto_property *properties)
  131. {
  132. int identifier;
  133. uint8_t i8value = 0;
  134. uint16_t i16value = 0;
  135. uint32_t i32value = 0;
  136. char *strname = NULL, *strvalue = NULL;
  137. char *binvalue = NULL;
  138. cJSON *tmp, *prop_json, *user_json = NULL;
  139. const mosquitto_property *prop = NULL;
  140. prop_json = cJSON_CreateObject();
  141. if(prop_json == NULL){
  142. cJSON_Delete(prop_json);
  143. return MOSQ_ERR_NOMEM;
  144. }
  145. cJSON_AddItemToObject(root, "properties", prop_json);
  146. for(prop=properties; prop != NULL; prop = mosquitto_property_next(prop)){
  147. tmp = NULL;
  148. identifier = mosquitto_property_identifier(prop);
  149. switch(identifier){
  150. case MQTT_PROP_PAYLOAD_FORMAT_INDICATOR:
  151. mosquitto_property_read_byte(prop, MQTT_PROP_PAYLOAD_FORMAT_INDICATOR, &i8value, false);
  152. tmp = cJSON_CreateNumber(i8value);
  153. break;
  154. case MQTT_PROP_MESSAGE_EXPIRY_INTERVAL:
  155. mosquitto_property_read_int32(prop, MQTT_PROP_MESSAGE_EXPIRY_INTERVAL, &i32value, false);
  156. tmp = cJSON_CreateNumber(i32value);
  157. break;
  158. case MQTT_PROP_CONTENT_TYPE:
  159. case MQTT_PROP_RESPONSE_TOPIC:
  160. mosquitto_property_read_string(prop, identifier, &strvalue, false);
  161. if(strvalue == NULL) return MOSQ_ERR_NOMEM;
  162. tmp = cJSON_CreateString(strvalue);
  163. free(strvalue);
  164. strvalue = NULL;
  165. break;
  166. case MQTT_PROP_CORRELATION_DATA:
  167. mosquitto_property_read_binary(prop, MQTT_PROP_CORRELATION_DATA, (void **)&binvalue, &i16value, false);
  168. if(binvalue == NULL) return MOSQ_ERR_NOMEM;
  169. tmp = cJSON_CreateString(binvalue);
  170. free(binvalue);
  171. binvalue = NULL;
  172. break;
  173. case MQTT_PROP_SUBSCRIPTION_IDENTIFIER:
  174. mosquitto_property_read_varint(prop, MQTT_PROP_SUBSCRIPTION_IDENTIFIER, &i32value, false);
  175. tmp = cJSON_CreateNumber(i32value);
  176. break;
  177. case MQTT_PROP_TOPIC_ALIAS:
  178. mosquitto_property_read_int16(prop, MQTT_PROP_MESSAGE_EXPIRY_INTERVAL, &i16value, false);
  179. tmp = cJSON_CreateNumber(i16value);
  180. break;
  181. case MQTT_PROP_USER_PROPERTY:
  182. if(user_json == NULL){
  183. user_json = cJSON_CreateObject();
  184. if(user_json == NULL){
  185. return MOSQ_ERR_NOMEM;
  186. }
  187. cJSON_AddItemToObject(prop_json, "user-properties", user_json);
  188. }
  189. mosquitto_property_read_string_pair(prop, MQTT_PROP_USER_PROPERTY, &strname, &strvalue, false);
  190. if(strname == NULL || strvalue == NULL) return MOSQ_ERR_NOMEM;
  191. tmp = cJSON_CreateString(strvalue);
  192. free(strvalue);
  193. if(tmp == NULL){
  194. free(strname);
  195. return MOSQ_ERR_NOMEM;
  196. }
  197. cJSON_AddItemToObject(user_json, strname, tmp);
  198. free(strname);
  199. strname = NULL;
  200. strvalue = NULL;
  201. tmp = NULL; /* Don't add this to prop_json below */
  202. break;
  203. }
  204. if(tmp != NULL){
  205. cJSON_AddItemToObject(prop_json, mosquitto_property_identifier_to_string(identifier), tmp);
  206. }
  207. }
  208. return MOSQ_ERR_SUCCESS;
  209. }
  210. #endif
  211. static void format_time_8601(const struct tm *ti, int ns, char *buf, size_t len)
  212. {
  213. char c;
  214. strftime(buf, len, "%Y-%m-%dT%H:%M:%S.000000%z", ti);
  215. c = buf[strlen("2020-05-06T21:48:00.000000")];
  216. snprintf(&buf[strlen("2020-05-06T21:48:00.")], 9, "%06d", ns/1000);
  217. buf[strlen("2020-05-06T21:48:00.000000")] = c;
  218. }
  219. static int json_print(const struct mosquitto_message *message, const mosquitto_property *properties, const struct tm *ti, int ns, bool escaped, bool pretty)
  220. {
  221. char buf[100];
  222. #ifdef WITH_CJSON
  223. cJSON *root;
  224. cJSON *tmp;
  225. char *json_str;
  226. const char *return_parse_end;
  227. root = cJSON_CreateObject();
  228. if(root == NULL){
  229. return MOSQ_ERR_NOMEM;
  230. }
  231. format_time_8601(ti, ns, buf, sizeof(buf));
  232. tmp = cJSON_CreateStringReference(buf);
  233. if(tmp == NULL){
  234. cJSON_Delete(root);
  235. return MOSQ_ERR_NOMEM;
  236. }
  237. cJSON_AddItemToObject(root, "tst", tmp);
  238. tmp = cJSON_CreateString(message->topic);
  239. if(tmp == NULL){
  240. cJSON_Delete(root);
  241. return MOSQ_ERR_NOMEM;
  242. }
  243. cJSON_AddItemToObject(root, "topic", tmp);
  244. tmp = cJSON_CreateNumber(message->qos);
  245. if(tmp == NULL){
  246. cJSON_Delete(root);
  247. return MOSQ_ERR_NOMEM;
  248. }
  249. cJSON_AddItemToObject(root, "qos", tmp);
  250. tmp = cJSON_CreateNumber(message->retain);
  251. if(tmp == NULL){
  252. cJSON_Delete(root);
  253. return MOSQ_ERR_NOMEM;
  254. }
  255. cJSON_AddItemToObject(root, "retain", tmp);
  256. tmp = cJSON_CreateNumber(message->payloadlen);
  257. if(tmp == NULL){
  258. cJSON_Delete(root);
  259. return MOSQ_ERR_NOMEM;
  260. }
  261. cJSON_AddItemToObject(root, "payloadlen", tmp);
  262. if(message->qos > 0){
  263. tmp = cJSON_CreateNumber(message->mid);
  264. if(tmp == NULL){
  265. cJSON_Delete(root);
  266. return MOSQ_ERR_NOMEM;
  267. }
  268. cJSON_AddItemToObject(root, "mid", tmp);
  269. }
  270. /* Properties */
  271. if(properties){
  272. if(json_print_properties(root, properties)){
  273. cJSON_Delete(root);
  274. return MOSQ_ERR_NOMEM;
  275. }
  276. }
  277. /* Payload */
  278. if(escaped){
  279. if(message->payload){
  280. tmp = cJSON_CreateString(message->payload);
  281. }else{
  282. tmp = cJSON_CreateNull();
  283. }
  284. if(tmp == NULL){
  285. cJSON_Delete(root);
  286. return MOSQ_ERR_NOMEM;
  287. }
  288. cJSON_AddItemToObject(root, "payload", tmp);
  289. }else{
  290. return_parse_end = NULL;
  291. if(message->payload){
  292. tmp = cJSON_ParseWithOpts(message->payload, &return_parse_end, true);
  293. if(tmp == NULL || return_parse_end != (char *)message->payload + message->payloadlen){
  294. cJSON_Delete(root);
  295. return MOSQ_ERR_INVAL;
  296. }
  297. }else{
  298. tmp = cJSON_CreateNull();
  299. if(tmp == NULL){
  300. cJSON_Delete(root);
  301. return MOSQ_ERR_INVAL;
  302. }
  303. }
  304. cJSON_AddItemToObject(root, "payload", tmp);
  305. }
  306. if(pretty){
  307. json_str = cJSON_Print(root);
  308. }else{
  309. json_str = cJSON_PrintUnformatted(root);
  310. }
  311. cJSON_Delete(root);
  312. if(json_str == NULL){
  313. return MOSQ_ERR_NOMEM;
  314. }
  315. fputs(json_str, stdout);
  316. free(json_str);
  317. return MOSQ_ERR_SUCCESS;
  318. #else
  319. UNUSED(properties);
  320. UNUSED(pretty);
  321. format_time_8601(ti, ns, buf, sizeof(buf));
  322. printf("{\"tst\":\"%s\",\"topic\":\"%s\",\"qos\":%d,\"retain\":%d,\"payloadlen\":%d,", buf, message->topic, message->qos, message->retain, message->payloadlen);
  323. if(message->qos > 0){
  324. printf("\"mid\":%d,", message->mid);
  325. }
  326. if(escaped){
  327. fputs("\"payload\":\"", stdout);
  328. write_json_payload(message->payload, message->payloadlen);
  329. fputs("\"}", stdout);
  330. }else{
  331. fputs("\"payload\":", stdout);
  332. write_payload(message->payload, message->payloadlen, 0, 0, 0, 0, 0);
  333. fputs("}", stdout);
  334. }
  335. return MOSQ_ERR_SUCCESS;
  336. #endif
  337. }
  338. static void formatted_print_blank(char pad, int field_width)
  339. {
  340. int i;
  341. for(i=0; i<field_width; i++){
  342. putchar(pad);
  343. }
  344. }
  345. static void formatted_print_int(int value, char align, char pad, int field_width)
  346. {
  347. if(field_width == 0){
  348. printf("%d", value);
  349. }else{
  350. if(align == '-'){
  351. printf("%-*d", field_width, value);
  352. }else{
  353. if(pad == '0'){
  354. printf("%0*d", field_width, value);
  355. }else{
  356. printf("%*d", field_width, value);
  357. }
  358. }
  359. }
  360. }
  361. static void formatted_print_str(const char *value, char align, int field_width, int precision)
  362. {
  363. if(field_width == 0 && precision == -1){
  364. fputs(value, stdout);
  365. }else{
  366. if(precision == -1){
  367. if(align == '-'){
  368. printf("%-*s", field_width, value);
  369. }else{
  370. printf("%*s", field_width, value);
  371. }
  372. }else if(field_width == 0){
  373. if(align == '-'){
  374. printf("%-.*s", precision, value);
  375. }else{
  376. printf("%.*s", precision, value);
  377. }
  378. }else{
  379. if(align == '-'){
  380. printf("%-*.*s", field_width, precision, value);
  381. }else{
  382. printf("%*.*s", field_width, precision, value);
  383. }
  384. }
  385. }
  386. }
  387. 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)
  388. {
  389. struct tm *ti = NULL;
  390. long ns = 0;
  391. char buf[100];
  392. int rc;
  393. uint8_t i8value;
  394. uint16_t i16value;
  395. uint32_t i32value;
  396. char *binvalue = NULL, *strname, *strvalue;
  397. const mosquitto_property *prop;
  398. switch(format){
  399. case '%':
  400. fputc('%', stdout);
  401. break;
  402. case 'A':
  403. if(mosquitto_property_read_int16(properties, MQTT_PROP_TOPIC_ALIAS, &i16value, false)){
  404. formatted_print_int(i16value, align, pad, field_width);
  405. }else{
  406. formatted_print_blank(pad, field_width);
  407. }
  408. break;
  409. case 'C':
  410. if(mosquitto_property_read_string(properties, MQTT_PROP_CONTENT_TYPE, &strvalue, false)){
  411. formatted_print_str(strvalue, align, field_width, precision);
  412. free(strvalue);
  413. }else{
  414. formatted_print_blank(' ', field_width);
  415. }
  416. break;
  417. case 'D':
  418. if(mosquitto_property_read_binary(properties, MQTT_PROP_CORRELATION_DATA, (void **)&binvalue, &i16value, false)){
  419. fwrite(binvalue, 1, i16value, stdout);
  420. free(binvalue);
  421. }
  422. break;
  423. case 'E':
  424. if(mosquitto_property_read_int32(properties, MQTT_PROP_MESSAGE_EXPIRY_INTERVAL, &i32value, false)){
  425. formatted_print_int((int)i32value, align, pad, field_width);
  426. }else{
  427. formatted_print_blank(pad, field_width);
  428. }
  429. break;
  430. case 'F':
  431. if(mosquitto_property_read_byte(properties, MQTT_PROP_PAYLOAD_FORMAT_INDICATOR, &i8value, false)){
  432. formatted_print_int(i8value, align, pad, field_width);
  433. }else{
  434. formatted_print_blank(pad, field_width);
  435. }
  436. break;
  437. case 'I':
  438. if(!ti){
  439. if(get_time(&ti, &ns)){
  440. err_printf(lcfg, "Error obtaining system time.\n");
  441. return;
  442. }
  443. }
  444. if(strftime(buf, 100, "%FT%T%z", ti) != 0){
  445. formatted_print_str(buf, align, field_width, precision);
  446. }else{
  447. formatted_print_blank(' ', field_width);
  448. }
  449. break;
  450. case 'j':
  451. if(!ti){
  452. if(get_time(&ti, &ns)){
  453. err_printf(lcfg, "Error obtaining system time.\n");
  454. return;
  455. }
  456. }
  457. if(json_print(message, properties, ti, (int)ns, true, lcfg->pretty) != MOSQ_ERR_SUCCESS){
  458. err_printf(lcfg, "Error: Out of memory.\n");
  459. return;
  460. }
  461. break;
  462. case 'J':
  463. if(!ti){
  464. if(get_time(&ti, &ns)){
  465. err_printf(lcfg, "Error obtaining system time.\n");
  466. return;
  467. }
  468. }
  469. rc = json_print(message, properties, ti, (int)ns, false, lcfg->pretty);
  470. if(rc == MOSQ_ERR_NOMEM){
  471. err_printf(lcfg, "Error: Out of memory.\n");
  472. return;
  473. }else if(rc == MOSQ_ERR_INVAL){
  474. err_printf(lcfg, "Error: Message payload is not valid JSON on topic %s.\n", message->topic);
  475. return;
  476. }
  477. break;
  478. case 'l':
  479. formatted_print_int(message->payloadlen, align, pad, field_width);
  480. break;
  481. case 'm':
  482. formatted_print_int(message->mid, align, pad, field_width);
  483. break;
  484. case 'P':
  485. strname = NULL;
  486. strvalue = NULL;
  487. prop = mosquitto_property_read_string_pair(properties, MQTT_PROP_USER_PROPERTY, &strname, &strvalue, false);
  488. while(prop){
  489. printf("%s:%s", strname, strvalue);
  490. free(strname);
  491. free(strvalue);
  492. strname = NULL;
  493. strvalue = NULL;
  494. prop = mosquitto_property_read_string_pair(prop, MQTT_PROP_USER_PROPERTY, &strname, &strvalue, true);
  495. if(prop){
  496. fputc(' ', stdout);
  497. }
  498. }
  499. free(strname);
  500. free(strvalue);
  501. break;
  502. case 'p':
  503. write_payload(message->payload, message->payloadlen, 0, align, pad, field_width, precision);
  504. break;
  505. case 'q':
  506. fputc(message->qos + 48, stdout);
  507. break;
  508. case 'R':
  509. if(mosquitto_property_read_string(properties, MQTT_PROP_RESPONSE_TOPIC, &strvalue, false)){
  510. formatted_print_str(strvalue, align, field_width, precision);
  511. free(strvalue);
  512. }
  513. break;
  514. case 'r':
  515. if(message->retain){
  516. fputc('1', stdout);
  517. }else{
  518. fputc('0', stdout);
  519. }
  520. break;
  521. case 'S':
  522. if(mosquitto_property_read_varint(properties, MQTT_PROP_SUBSCRIPTION_IDENTIFIER, &i32value, false)){
  523. formatted_print_int((int)i32value, align, pad, field_width);
  524. }else{
  525. formatted_print_blank(pad, field_width);
  526. }
  527. break;
  528. case 't':
  529. formatted_print_str(message->topic, align, field_width, precision);
  530. break;
  531. case 'U':
  532. if(!ti){
  533. if(get_time(&ti, &ns)){
  534. err_printf(lcfg, "Error obtaining system time.\n");
  535. return;
  536. }
  537. }
  538. if(strftime(buf, 100, "%s", ti) != 0){
  539. printf("%s.%09ld", buf, ns);
  540. }
  541. break;
  542. case 'x':
  543. write_payload(message->payload, message->payloadlen, 1, align, pad, field_width, precision);
  544. break;
  545. case 'X':
  546. write_payload(message->payload, message->payloadlen, 2, align, pad, field_width, precision);
  547. break;
  548. }
  549. }
  550. static void formatted_print(const struct mosq_config *lcfg, const struct mosquitto_message *message, const mosquitto_property *properties)
  551. {
  552. size_t len;
  553. size_t i;
  554. struct tm *ti = NULL;
  555. long ns = 0;
  556. char strf[3] = {0, 0 ,0};
  557. char buf[100];
  558. char align, pad;
  559. int field_width, precision;
  560. len = strlen(lcfg->format);
  561. for(i=0; i<len; i++){
  562. if(lcfg->format[i] == '%'){
  563. align = 0;
  564. pad = ' ';
  565. field_width = 0;
  566. precision = -1;
  567. if(i < len-1){
  568. i++;
  569. /* Optional alignment */
  570. if(lcfg->format[i] == '-'){
  571. align = lcfg->format[i];
  572. if(i < len-1){
  573. i++;
  574. }
  575. }
  576. /* "%-040p" is allowed by this combination of checks, but isn't
  577. * a valid format specifier, the '0' will be ignored. */
  578. /* Optional zero padding */
  579. if(lcfg->format[i] == '0'){
  580. pad = '0';
  581. if(i < len-1){
  582. i++;
  583. }
  584. }
  585. /* Optional field width */
  586. while(i < len-1 && lcfg->format[i] >= '0' && lcfg->format[i] <= '9'){
  587. field_width *= 10;
  588. field_width += lcfg->format[i]-'0';
  589. i++;
  590. }
  591. /* Optional precision */
  592. if(lcfg->format[i] == '.'){
  593. if(i < len-1){
  594. i++;
  595. precision = 0;
  596. while(i < len-1 && lcfg->format[i] >= '0' && lcfg->format[i] <= '9'){
  597. precision *= 10;
  598. precision += lcfg->format[i]-'0';
  599. i++;
  600. }
  601. }
  602. }
  603. if(i < len){
  604. formatted_print_percent(lcfg, message, properties, lcfg->format[i], align, pad, field_width, precision);
  605. }
  606. }
  607. }else if(lcfg->format[i] == '@'){
  608. if(i < len-1){
  609. i++;
  610. if(lcfg->format[i] == '@'){
  611. fputc('@', stdout);
  612. }else{
  613. if(!ti){
  614. if(get_time(&ti, &ns)){
  615. err_printf(lcfg, "Error obtaining system time.\n");
  616. return;
  617. }
  618. }
  619. strf[0] = '%';
  620. strf[1] = lcfg->format[i];
  621. strf[2] = 0;
  622. if(lcfg->format[i] == 'N'){
  623. printf("%09ld", ns);
  624. }else{
  625. if(strftime(buf, 100, strf, ti) != 0){
  626. fputs(buf, stdout);
  627. }
  628. }
  629. }
  630. }
  631. }else if(lcfg->format[i] == '\\'){
  632. if(i < len-1){
  633. i++;
  634. switch(lcfg->format[i]){
  635. case '\\':
  636. fputc('\\', stdout);
  637. break;
  638. case '0':
  639. fputc('\0', stdout);
  640. break;
  641. case 'a':
  642. fputc('\a', stdout);
  643. break;
  644. case 'e':
  645. fputc('\033', stdout);
  646. break;
  647. case 'n':
  648. fputc('\n', stdout);
  649. break;
  650. case 'r':
  651. fputc('\r', stdout);
  652. break;
  653. case 't':
  654. fputc('\t', stdout);
  655. break;
  656. case 'v':
  657. fputc('\v', stdout);
  658. break;
  659. }
  660. }
  661. }else{
  662. fputc(lcfg->format[i], stdout);
  663. }
  664. }
  665. if(lcfg->eol){
  666. fputc('\n', stdout);
  667. }
  668. fflush(stdout);
  669. }
  670. void output_init(void)
  671. {
  672. #ifndef WIN32
  673. struct tm *ti = NULL;
  674. long ns;
  675. if(!get_time(&ti, &ns)){
  676. srandom((unsigned int)ns);
  677. }
  678. #else
  679. /* Disable text translation so binary payloads aren't modified */
  680. _setmode(_fileno(stdout), _O_BINARY);
  681. #endif
  682. }
  683. void print_message(struct mosq_config *lcfg, const struct mosquitto_message *message, const mosquitto_property *properties)
  684. {
  685. #ifdef WIN32
  686. unsigned int r = 0;
  687. #else
  688. long r = 0;
  689. #endif
  690. if(lcfg->random_filter < 10000){
  691. #ifdef WIN32
  692. rand_s(&r);
  693. #else
  694. r = random();
  695. #endif
  696. if((long)(r%10000) >= lcfg->random_filter){
  697. return;
  698. }
  699. }
  700. if(lcfg->format){
  701. formatted_print(lcfg, message, properties);
  702. }else if(lcfg->verbose){
  703. if(message->payloadlen){
  704. printf("%s ", message->topic);
  705. write_payload(message->payload, message->payloadlen, false, 0, 0, 0, 0);
  706. if(lcfg->eol){
  707. printf("\n");
  708. }
  709. }else{
  710. if(lcfg->eol){
  711. printf("%s (null)\n", message->topic);
  712. }
  713. }
  714. fflush(stdout);
  715. }else{
  716. if(message->payloadlen){
  717. write_payload(message->payload, message->payloadlen, false, 0, 0, 0, 0);
  718. if(lcfg->eol){
  719. printf("\n");
  720. }
  721. fflush(stdout);
  722. }
  723. }
  724. }