nrf_cli.c 117 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711
  1. /**
  2. * Copyright (c) 2016 - 2018, Nordic Semiconductor ASA
  3. *
  4. * All rights reserved.
  5. *
  6. * Redistribution and use in source and binary forms, with or without modification,
  7. * are permitted provided that the following conditions are met:
  8. *
  9. * 1. Redistributions of source code must retain the above copyright notice, this
  10. * list of conditions and the following disclaimer.
  11. *
  12. * 2. Redistributions in binary form, except as embedded into a Nordic
  13. * Semiconductor ASA integrated circuit in a product or a software update for
  14. * such product, must reproduce the above copyright notice, this list of
  15. * conditions and the following disclaimer in the documentation and/or other
  16. * materials provided with the distribution.
  17. *
  18. * 3. Neither the name of Nordic Semiconductor ASA nor the names of its
  19. * contributors may be used to endorse or promote products derived from this
  20. * software without specific prior written permission.
  21. *
  22. * 4. This software, with or without modification, must only be used with a
  23. * Nordic Semiconductor ASA integrated circuit.
  24. *
  25. * 5. Any software provided in binary form under this license must not be reverse
  26. * engineered, decompiled, modified and/or disassembled.
  27. *
  28. * THIS SOFTWARE IS PROVIDED BY NORDIC SEMICONDUCTOR ASA "AS IS" AND ANY EXPRESS
  29. * OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
  30. * OF MERCHANTABILITY, NONINFRINGEMENT, AND FITNESS FOR A PARTICULAR PURPOSE ARE
  31. * DISCLAIMED. IN NO EVENT SHALL NORDIC SEMICONDUCTOR ASA OR CONTRIBUTORS BE
  32. * LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
  33. * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE
  34. * GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
  35. * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
  36. * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
  37. * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
  38. *
  39. */
  40. #include "sdk_common.h"
  41. #if NRF_MODULE_ENABLED(NRF_CLI)
  42. #include <ctype.h>
  43. #include <stdarg.h>
  44. #include <stdlib.h>
  45. #include "nrf_cli.h"
  46. #include "nrf_cli_vt100.h"
  47. #include "app_error.h"
  48. #include "nrf_assert.h"
  49. #include "nrf_delay.h"
  50. #include "nrf_pwr_mgmt.h"
  51. #include "nrf_atomic.h"
  52. #if NRF_MODULE_ENABLED(NRF_CLI_WILDCARD)
  53. #include "fnmatch.h"
  54. #endif
  55. #if NRF_MODULE_ENABLED(NRF_CLI_HISTORY)
  56. #if NRF_CLI_HISTORY_ELEMENT_SIZE * NRF_CLI_HISTORY_ELEMENT_COUNT == 0
  57. #error Not proper memory size allocated for NRF_CLI_HISTORY
  58. #endif
  59. #endif
  60. /* 2 == 1 char for cmd + 1 char for '\0' */
  61. #if NRF_CLI_CMD_BUFF_SIZE < 2
  62. #error too small NRF_CLI_CMD_BUFF_SIZE
  63. #endif
  64. #if NRF_CLI_PRINTF_BUFF_SIZE < 1
  65. #error too small NRF_CLI_PRINTF_BUFF_SIZE
  66. #endif
  67. #define NRF_CLI_HELP_CLEAR "Clear screen."
  68. #define NRF_CLI_HELP_COLORS "Toggle colored syntax."
  69. #define NRF_CLI_HELP_COLORS_OFF "Disable colored syntax."
  70. #define NRF_CLI_HELP_COLORS_ON "Enable colored syntax."
  71. #define NRF_CLI_HELP_STATISTICS "CLI statistics."
  72. #define NRF_CLI_HELP_STATISTICS_SHOW "Get CLI statistics for the Logger module."
  73. #define NRF_CLI_HELP_STATISTICS_RESET "Reset CLI statistics for the Logger module."
  74. #define NRF_CLI_HELP_RESIZE "Console gets terminal screen size or assumes 80 in case " \
  75. "the readout fails. It must be executed after each terminal " \
  76. "width change to ensure correct text display."
  77. #define NRF_CLI_HELP_RESIZE_DEFAULT "Assume 80 chars screen width and send this setting " \
  78. "to the terminal."
  79. #define NRF_CLI_HELP_HISTORY "Command history."
  80. #define NRF_CLI_HELP_ECHO "Toggle CLI echo."
  81. #define NRF_CLI_HELP_ECHO_ON "Enable CLI echo."
  82. #define NRF_CLI_HELP_ECHO_OFF "Disable CLI echo. Arrows and buttons: Backspace, " \
  83. "Delete, End, Home, Insert are not handled."
  84. #define NRF_CLI_HELP_CLI "Useful, not Unix-like CLI commands."
  85. #define NRF_CLI_MSG_SPECIFY_SUBCOMMAND "Please specify a subcommand.\r\n"
  86. #define NRF_CLI_MSG_UNKNOWN_PARAMETER " unknown parameter: "
  87. #define NRF_CLI_MSG_COMMAND_NOT_FOUND ": command not found"
  88. #define NRF_CLI_MSG_TAB_OVERFLOWED "Tab function: commands counter overflowed.\r\n"
  89. /*lint -save -esym(526,cli_command*) -esym(526,cli_sorted_cmd_ptrs*)*/
  90. NRF_SECTION_DEF(cli_command, nrf_cli_cmd_entry_t);
  91. #define CLI_DATA_SECTION_ITEM_GET(i) NRF_SECTION_ITEM_GET(cli_command, nrf_cli_cmd_entry_t, (i))
  92. #define CLI_DATA_SECTION_ITEM_COUNT NRF_SECTION_ITEM_COUNT(cli_command, nrf_cli_cmd_entry_t)
  93. NRF_SECTION_DEF(cli_sorted_cmd_ptrs, const char *);
  94. /*lint -restore*/
  95. #define CLI_SORTED_CMD_PTRS_ITEM_GET(i) NRF_SECTION_ITEM_GET(cli_sorted_cmd_ptrs, const char *, (i))
  96. #define CLI_SORTED_CMD_PTRS_START_ADDR_GET NRF_SECTION_START_ADDR(cli_sorted_cmd_ptrs)
  97. #if defined(NRF_CLI_LOG_BACKEND) && NRF_CLI_LOG_BACKEND
  98. #include "nrf_log_str_formatter.h"
  99. #include "nrf_log_internal.h"
  100. #endif
  101. #define NRF_CLI_INIT_OPTION_PRINTER (NULL)
  102. #define NRF_CLI_MAX_TERMINAL_SIZE (250u)
  103. #define NRF_CLI_CURSOR_POSITION_BUFFER (10u) /* 10 == {esc, [, 2, 5, 0, ;, 2, 5, 0, '\0'} */
  104. #define NRF_CLI_DEFAULT_TERMINAL_WIDTH (80u) /* Default PuTTY width. */
  105. #define NRF_CLI_DEFAULT_TERMINAL_HEIGHT (24u) /* Default PuTTY height. */
  106. #define NRF_CLI_INITIAL_CURS_POS (1u) /* Initial cursor position is: (1, 1). */
  107. #define NRF_CLI_CMD_ROOT_LVL (0u)
  108. /* Macro to send VT100 commands. */
  109. #define NRF_CLI_VT100_CMD(_p_cli_, _cmd_) { \
  110. ASSERT(_p_cli_); \
  111. ASSERT(_p_cli_->p_fprintf_ctx); \
  112. static char const cmd[] = _cmd_; \
  113. nrf_fprintf(_p_cli_->p_fprintf_ctx, "%s", cmd); \
  114. }
  115. #if NRF_MODULE_ENABLED(NRF_CLI_WILDCARD)
  116. typedef enum
  117. {
  118. WILDCARD_CMD_ADDED,
  119. WILDCARD_CMD_ADDED_MISSING_SPACE,
  120. WILDCARD_CMD_NO_MATCH_FOUND
  121. } wildcard_cmd_status_t;
  122. #endif
  123. static bool cli_log_entry_process(nrf_cli_t const * p_cli, bool skip);
  124. static void cli_execute(nrf_cli_t const * p_cli);
  125. static inline void transport_buffer_flush(nrf_cli_t const * p_cli)
  126. {
  127. nrf_fprintf_buffer_flush(p_cli->p_fprintf_ctx);
  128. }
  129. static inline void cli_flag_help_set(nrf_cli_t const * p_cli)
  130. {
  131. p_cli->p_ctx->internal.flag.show_help = 1;
  132. }
  133. static inline void cli_flag_help_clear(nrf_cli_t const * p_cli)
  134. {
  135. p_cli->p_ctx->internal.flag.show_help = 0;
  136. }
  137. static inline void cli_flag_echo_set(nrf_cli_t const * p_cli)
  138. {
  139. p_cli->p_ctx->internal.flag.echo = 1;
  140. }
  141. static inline void cli_flag_echo_clear(nrf_cli_t const * p_cli)
  142. {
  143. p_cli->p_ctx->internal.flag.echo = 0;
  144. }
  145. static inline bool cli_flag_echo_is_set(nrf_cli_t const * p_cli)
  146. {
  147. return p_cli->p_ctx->internal.flag.echo == 1 ? true : false;
  148. }
  149. static inline bool cli_flag_processing_is_set(nrf_cli_t const * p_cli)
  150. {
  151. return p_cli->p_ctx->internal.flag.processing == 1 ? true : false;
  152. }
  153. static inline void receive_state_change(nrf_cli_t const * p_cli, nrf_cli_receive_t state)
  154. {
  155. p_cli->p_ctx->receive_state = state;
  156. }
  157. static inline size_t cli_strlen(char const * str)
  158. {
  159. return str == NULL ? 0 : strlen(str);
  160. }
  161. static void cli_cmd_buffer_clear(nrf_cli_t const * p_cli)
  162. {
  163. p_cli->p_ctx->cmd_buff[0] = '\0'; /* clear command buffer */
  164. p_cli->p_ctx->cmd_buff_pos = 0;
  165. p_cli->p_ctx->cmd_buff_len = 0;
  166. }
  167. /* Function returns true if cursor is at beginning of an empty line. */
  168. static inline bool cursor_in_empty_line(nrf_cli_t const * p_cli)
  169. {
  170. return ( (p_cli->p_ctx->cmd_buff_pos + cli_strlen(p_cli->p_name)) %
  171. p_cli->p_ctx->vt100_ctx.cons.terminal_wid == 0);
  172. }
  173. /* Function returns true if command length is equal to multiplicity of terminal width. */
  174. static inline bool full_line_cmd(nrf_cli_t const * p_cli)
  175. {
  176. return ((p_cli->p_ctx->cmd_buff_len + cli_strlen(p_cli->p_name)) %
  177. p_cli->p_ctx->vt100_ctx.cons.terminal_wid == 0);
  178. }
  179. #if NRF_MODULE_ENABLED(NRF_CLI_WILDCARD)
  180. /* Function returns true if string contains wildcard character: '?' or '*'. */
  181. static bool wildcard_character_exist(char * p_str)
  182. {
  183. size_t str_len = cli_strlen(p_str);
  184. for (size_t i = 0; i < str_len; i++)
  185. {
  186. if ((p_str[i] == '?') || (p_str[i] == '*'))
  187. {
  188. return true;
  189. }
  190. }
  191. return false;
  192. }
  193. #endif
  194. /* Function sends data stream to the CLI instance. Each time before the cli_write function is called,
  195. * it must be ensured that IO buffer of fprintf is flushed to avoid synchronization issues.
  196. * For that purpose, use function transport_buffer_flush(p_cli) */
  197. static void cli_write(nrf_cli_t const * p_cli,
  198. void const * p_data,
  199. size_t length,
  200. size_t * p_cnt)
  201. {
  202. ASSERT(p_cli && p_data);
  203. ASSERT(p_cli->p_iface->p_api);
  204. size_t offset = 0;
  205. size_t cnt;
  206. while (length)
  207. {
  208. ret_code_t ret = p_cli->p_iface->p_api->write(p_cli->p_iface,
  209. &((uint8_t const *)p_data)[offset],
  210. length,
  211. &cnt);
  212. UNUSED_VARIABLE(ret);
  213. ASSERT(ret == NRF_SUCCESS);
  214. ASSERT(length >= cnt);
  215. offset += cnt;
  216. length -= cnt;
  217. if (cnt == 0 && (p_cli->p_ctx->state != NRF_CLI_STATE_PANIC_MODE_ACTIVE))
  218. {
  219. #if NRF_MODULE_ENABLED(NRF_CLI_USES_TASK_MANAGER)
  220. (void)task_events_wait(NRF_CLI_TRANSPORT_TX_RDY_TASK_EVT);
  221. #else
  222. while (p_cli->p_ctx->internal.flag.tx_rdy == 0)
  223. {
  224. ;
  225. }
  226. p_cli->p_ctx->internal.flag.tx_rdy = 0;
  227. #endif
  228. }
  229. }
  230. if (p_cnt)
  231. {
  232. *p_cnt = cnt;
  233. }
  234. }
  235. /* Function sends 1 character to the CLI instance. */
  236. static inline void cli_putc(nrf_cli_t const * p_cli, char ch)
  237. {
  238. nrf_fprintf(p_cli->p_fprintf_ctx, "%c", ch);
  239. }
  240. /* Function reads data from the CLI instance. */
  241. static void cli_read(nrf_cli_t const * p_cli,
  242. void * p_data,
  243. size_t length,
  244. size_t * p_cnt)
  245. {
  246. ASSERT(p_cli && p_data);
  247. ASSERT(p_cli->p_iface);
  248. ret_code_t ret = p_cli->p_iface->p_api->read(p_cli->p_iface, p_data, length, p_cnt);
  249. UNUSED_VARIABLE(ret);
  250. }
  251. /* Function cmd_get shall be used to search commands. It moves the pointer pp_entry to command
  252. * of static command structure. If the command cannot be found, the function will set pp_entry to NULL.
  253. * p_command Pointer to command which will be processed (no matter the root command).
  254. * lvl Level of the requested command.
  255. * idx Index of the requested command.
  256. * pp_entry Pointer which points to subcommand[idx] after function execution.
  257. * p_st_entry Pointer to the structure where dynamic entry data can be stored.
  258. */
  259. static void cmd_get(nrf_cli_cmd_entry_t const * p_command,
  260. size_t lvl,
  261. size_t idx,
  262. nrf_cli_static_entry_t const ** pp_entry,
  263. nrf_cli_static_entry_t * p_st_entry)
  264. {
  265. ASSERT (pp_entry != NULL);
  266. ASSERT (p_st_entry != NULL);
  267. if (lvl == NRF_CLI_CMD_ROOT_LVL)
  268. {
  269. if (idx < CLI_DATA_SECTION_ITEM_COUNT)
  270. {
  271. nrf_cli_cmd_entry_t const * p_cmd = NULL;
  272. char const * * pp_sorted_cmds = (char const * *)CLI_SORTED_CMD_PTRS_START_ADDR_GET;
  273. for (size_t i = 0; i < CLI_DATA_SECTION_ITEM_COUNT; i++)
  274. {
  275. p_cmd = CLI_DATA_SECTION_ITEM_GET(i);
  276. if (!strcmp(pp_sorted_cmds[idx], p_cmd->u.p_static->p_syntax))
  277. {
  278. *pp_entry = p_cmd->u.p_static;
  279. return;
  280. }
  281. }
  282. }
  283. *pp_entry = NULL;
  284. return;
  285. }
  286. if (p_command == NULL)
  287. {
  288. *pp_entry = NULL;
  289. return;
  290. }
  291. if (p_command->is_dynamic)
  292. {
  293. p_command->u.p_dynamic_get(idx, p_st_entry);
  294. if (p_st_entry->p_syntax == NULL)
  295. {
  296. *pp_entry = NULL;
  297. }
  298. else
  299. {
  300. *pp_entry = p_st_entry;
  301. }
  302. }
  303. else
  304. {
  305. if (p_command->u.p_static[idx].p_syntax != NULL)
  306. {
  307. *pp_entry = &p_command->u.p_static[idx];
  308. }
  309. else
  310. {
  311. *pp_entry = NULL;
  312. }
  313. }
  314. }
  315. /* Function multiline_console_data_check checks the current cursor position (x, y) on terminal screen
  316. * based on: command length, console name length, and terminal width.
  317. * Example 1:
  318. * || - cursor
  319. * ----------------------------
  320. * |console_name $: || |
  321. * ----------------------------
  322. * => coordinates are: cur_x = 17, cur_x_end = 17,
  323. * cur_y = 1, cur_y_end = 1
  324. * Example 2:
  325. * ----------------------------
  326. * |console_name $: test command|
  327. * |showing |e|xample |
  328. * ----------------------------
  329. * => coordinates are: cur_x = 9, cur_x_end = 18 (cursor can be one column after 'e')
  330. * => cur_y = 2, cur_y_end = 2
  331. * Example 3:
  332. * ----------------------------
  333. * |console_name $: test command|
  334. * |showing e|x|ample with more |
  335. * |parameters |
  336. * ----------------------------
  337. * => coordinates are: cur_x = 10, cur_x_end = 11 (cursor can be one column after 's')
  338. * => cur_y = 2, cur_y_end = 3
  339. */
  340. static nrf_cli_multiline_cons_t const * multiline_console_data_check(nrf_cli_t const * p_cli)
  341. {
  342. nrf_cli_ctx_t * p_ctx = p_cli->p_ctx;
  343. nrf_cli_multiline_cons_t * p_cons = &p_cli->p_ctx->vt100_ctx.cons;
  344. p_cons->name_len = cli_strlen(p_cli->p_name);
  345. /* Current cursor position in command.
  346. * +1 -> because home position is (1, 1) */
  347. p_cons->cur_x = (p_ctx->cmd_buff_pos + p_cons->name_len) % p_cons->terminal_wid + 1;
  348. p_cons->cur_y = (p_ctx->cmd_buff_pos + p_cons->name_len) / p_cons->terminal_wid + 1;
  349. /* Extreme position when cursor is at the end of command. */
  350. p_cons->cur_y_end = (p_ctx->cmd_buff_len + p_cons->name_len) / p_cons->terminal_wid + 1;
  351. p_cons->cur_x_end = (p_ctx->cmd_buff_len + p_cons->name_len) % p_cons->terminal_wid + 1;
  352. return p_cons;
  353. }
  354. #if NRF_MODULE_ENABLED(NRF_CLI_METAKEYS)
  355. /* Calculates relative line number of given position in buffer */
  356. static uint32_t cli_line_num_with_buffer_offset_get(nrf_cli_t const * p_cli,
  357. nrf_cli_cmd_len_t buffer_pos)
  358. {
  359. uint32_t name_len;
  360. nrf_cli_multiline_cons_t *p_cons = &p_cli->p_ctx->vt100_ctx.cons;
  361. name_len = cli_strlen(p_cli->p_name);
  362. return ((buffer_pos + name_len) / p_cons->terminal_wid);
  363. }
  364. #endif
  365. #if NRF_MODULE_ENABLED(NRF_CLI_METAKEYS)
  366. /* Calculates column number of given position in buffer */
  367. static uint32_t cli_col_num_with_buffer_offset_get(nrf_cli_t const * p_cli,
  368. nrf_cli_cmd_len_t buffer_pos)
  369. {
  370. uint32_t name_len;
  371. nrf_cli_multiline_cons_t *p_cons = &p_cli->p_ctx->vt100_ctx.cons;
  372. name_len = cli_strlen(p_cli->p_name);
  373. /* columns are counted from 1 */
  374. return (1 + ((buffer_pos + name_len) % p_cons->terminal_wid));
  375. }
  376. #endif
  377. #if NRF_MODULE_ENABLED(NRF_CLI_METAKEYS)
  378. /* For the given buffer, calculates row span between position2 and position1 */
  379. static int32_t cli_row_span_with_buffer_offsets_get(nrf_cli_t const * p_cli,
  380. nrf_cli_cmd_len_t offset1,
  381. nrf_cli_cmd_len_t offset2)
  382. {
  383. return cli_line_num_with_buffer_offset_get(p_cli, offset2)
  384. - cli_line_num_with_buffer_offset_get(p_cli, offset1);
  385. }
  386. #endif
  387. #if NRF_MODULE_ENABLED(NRF_CLI_METAKEYS)
  388. /* For the given buffer, calculates column span between position2 and position 1 */
  389. static int32_t cli_column_span_with_buffer_offsets_get(nrf_cli_t const * p_cli,
  390. nrf_cli_cmd_len_t offset1,
  391. nrf_cli_cmd_len_t offset2)
  392. {
  393. return cli_col_num_with_buffer_offset_get(p_cli, offset2)
  394. - cli_col_num_with_buffer_offset_get(p_cli, offset1);
  395. }
  396. #endif
  397. /* Function sends VT100 command to clear the screen from cursor position to end of the screen. */
  398. static inline void cli_clear_eos(nrf_cli_t const * p_cli)
  399. {
  400. NRF_CLI_VT100_CMD(p_cli, NRF_CLI_VT100_CLEAREOS);
  401. }
  402. /* Function sends VT100 command to save cursor position. */
  403. static inline void cli_cursor_save(nrf_cli_t const * p_cli)
  404. {
  405. NRF_CLI_VT100_CMD(p_cli, NRF_CLI_VT100_SAVECURSOR);
  406. }
  407. /* Function sends VT100 command to restore saved cursor position. */
  408. static inline void cli_cursor_restore(nrf_cli_t const * p_cli)
  409. {
  410. NRF_CLI_VT100_CMD(p_cli, NRF_CLI_VT100_RESTORECURSOR);
  411. }
  412. /* Function forcing new line - cannot be replaced with function cursor_down_move. */
  413. static inline void cursor_next_line_move(nrf_cli_t const * p_cli)
  414. {
  415. NRF_CLI_VT100_CMD(p_cli, NRF_CLI_VT100_NEXTLINE);
  416. }
  417. /* Function moves cursor left by n positions. */
  418. static inline void cursor_left_move(nrf_cli_t const * p_cli, nrf_cli_cmd_len_t n)
  419. {
  420. if (n > 0)
  421. {
  422. nrf_fprintf(p_cli->p_fprintf_ctx, "\033[%dD", n);
  423. }
  424. }
  425. /* Function moves cursor right by n positions. */
  426. static inline void cursor_right_move(nrf_cli_t const * p_cli, nrf_cli_cmd_len_t n)
  427. {
  428. if (n > 0)
  429. {
  430. nrf_fprintf(p_cli->p_fprintf_ctx, "\033[%dC", n);
  431. }
  432. }
  433. #if NRF_MODULE_ENABLED(NRF_CLI_METAKEYS)
  434. /* Moves cursor horizontally by a number. Positive is right */
  435. static void cursor_horiz_move(nrf_cli_t const * p_cli, int32_t delta)
  436. {
  437. if (delta > 0)
  438. {
  439. cursor_right_move(p_cli, delta);
  440. }
  441. else if (delta < 0)
  442. {
  443. cursor_left_move(p_cli, -delta);
  444. }
  445. else { }
  446. }
  447. #endif
  448. /* Function moves cursor up by n positions. */
  449. static inline void cursor_up_move(nrf_cli_t const * p_cli, nrf_cli_cmd_len_t n)
  450. {
  451. if (n > 0)
  452. {
  453. nrf_fprintf(p_cli->p_fprintf_ctx, "\033[%dA", n);
  454. }
  455. }
  456. /* Function moves cursor down by n positions but it will bring no effect if cursor is in the last
  457. * line of terminal screen. In such case, the cursor_next_line_move function shall be invoked. */
  458. static inline void cursor_down_move(nrf_cli_t const * p_cli, nrf_cli_cmd_len_t n)
  459. {
  460. if (n > 0)
  461. {
  462. nrf_fprintf(p_cli->p_fprintf_ctx, "\033[%dB", n);
  463. }
  464. }
  465. #if NRF_MODULE_ENABLED(NRF_CLI_METAKEYS)
  466. /* Moves cursor vertically by a number. Positive is down */
  467. static void cursor_vert_move(nrf_cli_t const * p_cli, int32_t delta)
  468. {
  469. if (delta > 0)
  470. {
  471. cursor_down_move(p_cli, delta);
  472. }
  473. else if (delta < 0)
  474. {
  475. cursor_up_move(p_cli, -delta);
  476. }
  477. else { }
  478. }
  479. #endif
  480. /* Function increments cursor position (if possible) and moves cursor to new line if necessary. */
  481. static void cursor_position_increment(nrf_cli_t const * p_cli)
  482. {
  483. if (p_cli->p_ctx->cmd_buff_pos >= p_cli->p_ctx->cmd_buff_len)
  484. {
  485. return; /* incrementation not possible */
  486. }
  487. nrf_cli_multiline_cons_t const * p_cons = multiline_console_data_check(p_cli);
  488. nrf_cli_cmd_len_t cur_y = p_cons->cur_y;
  489. ++p_cli->p_ctx->cmd_buff_pos;
  490. p_cons = multiline_console_data_check(p_cli);
  491. if (cur_y == p_cons->cur_y)
  492. {
  493. cursor_right_move(p_cli, 1);
  494. }
  495. else
  496. {
  497. cursor_next_line_move(p_cli);
  498. }
  499. }
  500. /* Function will move cursor back to position == cmd_buff_pos. Example usage is when cursor needs
  501. * to be moved back after printing some text. This function cannot be used to move cursor to new
  502. * location by manual change of cmd_buff_pos.*/
  503. static void cursor_position_synchronize(nrf_cli_t const * p_cli)
  504. {
  505. nrf_cli_multiline_cons_t const * p_cons = multiline_console_data_check(p_cli);
  506. bool last_line = p_cons->cur_y == p_cons->cur_y_end ? true : false;
  507. /* In case cursor reaches the bottom line of a terminal, it will be moved to the next line. */
  508. if (cursor_in_empty_line(p_cli) || full_line_cmd(p_cli))
  509. {
  510. cursor_next_line_move(p_cli);
  511. }
  512. if (last_line)
  513. {
  514. cursor_left_move(p_cli, p_cons->cur_x_end - p_cons->cur_x);
  515. }
  516. else
  517. {
  518. cursor_up_move(p_cli, p_cons->cur_y_end - p_cons->cur_y);
  519. if (p_cons->cur_x > p_cons->cur_x_end)
  520. {
  521. cursor_right_move(p_cli, p_cons->cur_x - p_cons->cur_x_end);
  522. }
  523. else
  524. {
  525. cursor_left_move(p_cli, p_cons->cur_x_end - p_cons->cur_x);
  526. }
  527. }
  528. }
  529. #if NRF_MODULE_ENABLED(NRF_CLI_METAKEYS)
  530. /**
  531. * Removes the "word" to the left of the cursor:
  532. * - if there are spaces at the cursor position, remove all spaces to the left
  533. * - remove the non-spaces (word) until a space is found or a beginning of buffer
  534. */
  535. static void cli_cmd_word_remove(nrf_cli_t const * p_cli)
  536. {
  537. nrf_cli_cmd_len_t new_pos;
  538. nrf_cli_cmd_len_t chars_to_delete;
  539. int32_t row_span;
  540. int32_t col_span;
  541. /* Line must not be empty and cursor must not be at 0 to continue */
  542. if ((p_cli->p_ctx->cmd_buff_len == 0) || (p_cli->p_ctx->cmd_buff_pos == 0))
  543. {
  544. return;
  545. }
  546. /* start at the current position */
  547. new_pos = p_cli->p_ctx->cmd_buff_pos;
  548. chars_to_delete = 0;
  549. /* look back for all spaces then for non-spaces */
  550. while ((new_pos >= 1) && (p_cli->p_ctx->cmd_buff[new_pos - 1] == ' '))
  551. {
  552. ++chars_to_delete;
  553. --new_pos;
  554. }
  555. while ((new_pos >= 1) && (p_cli->p_ctx->cmd_buff[new_pos - 1] != ' '))
  556. {
  557. --new_pos;
  558. ++chars_to_delete;
  559. }
  560. /* calculate the new cursor */
  561. row_span = cli_row_span_with_buffer_offsets_get(p_cli, p_cli->p_ctx->cmd_buff_pos, new_pos);
  562. col_span = cli_column_span_with_buffer_offsets_get(p_cli, p_cli->p_ctx->cmd_buff_pos, new_pos);
  563. /* manage the buffer */
  564. memmove(&p_cli->p_ctx->cmd_buff[new_pos],
  565. &p_cli->p_ctx->cmd_buff[new_pos + chars_to_delete],
  566. p_cli->p_ctx->cmd_buff_len - chars_to_delete);
  567. p_cli->p_ctx->cmd_buff_len -= chars_to_delete;
  568. p_cli->p_ctx->cmd_buff_pos = new_pos;
  569. p_cli->p_ctx->cmd_buff[p_cli->p_ctx->cmd_buff_len] = '\0';
  570. /* update display */
  571. cursor_horiz_move(p_cli, col_span);
  572. cursor_vert_move(p_cli, row_span);
  573. cli_cursor_save(p_cli);
  574. nrf_cli_fprintf(p_cli,
  575. NRF_CLI_NORMAL,
  576. "%s",
  577. &p_cli->p_ctx->cmd_buff[p_cli->p_ctx->cmd_buff_pos]);
  578. cli_clear_eos(p_cli);
  579. cli_cursor_restore(p_cli);
  580. }
  581. #endif
  582. #if NRF_MODULE_ENABLED(NRF_CLI_HISTORY) || NRF_MODULE_ENABLED(NRF_CLI_METAKEYS)
  583. /* Function moves cursor to begin of command position, just after console name. */
  584. static void cursor_home_position_move(nrf_cli_t const * p_cli)
  585. {
  586. nrf_cli_multiline_cons_t const * p_cons = multiline_console_data_check(p_cli);
  587. if ((p_cons->cur_x == p_cons->name_len + NRF_CLI_INITIAL_CURS_POS) &&
  588. (p_cons->cur_y == NRF_CLI_INITIAL_CURS_POS))
  589. {
  590. return; /* nothing to handle because cursor is in start position */
  591. }
  592. if (p_cons->cur_y > NRF_CLI_INITIAL_CURS_POS)
  593. {
  594. cursor_up_move(p_cli, p_cons->cur_y - NRF_CLI_INITIAL_CURS_POS);
  595. }
  596. if (p_cons->cur_x > p_cons->name_len)
  597. {
  598. cursor_left_move(p_cli, p_cons->cur_x - NRF_CLI_INITIAL_CURS_POS - p_cons->name_len);
  599. }
  600. else
  601. {
  602. cursor_right_move(p_cli, p_cons->name_len + NRF_CLI_INITIAL_CURS_POS - p_cons->cur_x);
  603. }
  604. /* align data buffer pointer with cursor position */
  605. p_cli->p_ctx->cmd_buff_pos = 0;
  606. }
  607. #endif
  608. /* Function moves cursor to end of command. */
  609. static void cursor_end_position_move(nrf_cli_t const * p_cli)
  610. {
  611. nrf_cli_multiline_cons_t const * p_cons = multiline_console_data_check(p_cli);
  612. if ((p_cons->cur_x == p_cons->cur_x_end) && (p_cons->cur_y == p_cons->cur_y_end))
  613. {
  614. return; /* nothing to handle because cursor is in end position */
  615. }
  616. if (p_cons->cur_y_end > p_cons->cur_y)
  617. {
  618. cursor_down_move(p_cli, p_cons->cur_y_end - p_cons->cur_y);
  619. }
  620. if (p_cons->cur_x > p_cons->cur_x_end)
  621. {
  622. cursor_left_move(p_cli, p_cons->cur_x - p_cons->cur_x_end);
  623. }
  624. else
  625. {
  626. cursor_right_move(p_cli, p_cons->cur_x_end - p_cons->cur_x);
  627. }
  628. /* align data buffer pointer with cursor position */
  629. p_cli->p_ctx->cmd_buff_pos = p_cli->p_ctx->cmd_buff_len;
  630. }
  631. #if NRF_MODULE_ENABLED(NRF_CLI_BUILD_IN_CMDS)
  632. /* Function reads cursor position from terminal. */
  633. static ret_code_t cursor_position_get(nrf_cli_t const * p_cli)
  634. {
  635. size_t cnt;
  636. uint16_t x = 0; /* horizontal position */
  637. uint16_t y = 0; /* vertical position */
  638. char c = 0;
  639. nrf_cli_cmd_len_t buff_idx = 0;
  640. /* clear temp buffer */
  641. memset(p_cli->p_ctx->temp_buff, 0, sizeof(p_cli->p_ctx->temp_buff));
  642. /* escape code asking terminal about its size */
  643. static char const cmd_get_terminal_size[] = "\033[6n";
  644. nrf_fprintf(p_cli->p_fprintf_ctx, cmd_get_terminal_size);
  645. /* fprintf buffer needs to be flushed to start sending prepared escape code to the terminal */
  646. transport_buffer_flush(p_cli);
  647. /* timeout for terminal response = ~1s */
  648. for (uint16_t i = 0; i < 1000; i++)
  649. {
  650. do
  651. {
  652. cli_read(p_cli, &c, sizeof(c), &cnt);
  653. if (cnt == 0)
  654. {
  655. nrf_delay_us(999);
  656. continue;
  657. }
  658. if ((c != NRF_CLI_VT100_ASCII_ESC) &&
  659. (p_cli->p_ctx->temp_buff[0] != NRF_CLI_VT100_ASCII_ESC))
  660. {
  661. continue;
  662. }
  663. if (c == 'R') /* end of response from the terminal */
  664. {
  665. p_cli->p_ctx->temp_buff[buff_idx] = '\0';
  666. if (p_cli->p_ctx->temp_buff[1] != '[')
  667. {
  668. p_cli->p_ctx->temp_buff[0] = 0;
  669. return NRF_ERROR_INVALID_DATA;
  670. }
  671. buff_idx = 2; /* index start position in the buffer where 'y' is stored */
  672. while (p_cli->p_ctx->temp_buff[buff_idx] != ';')
  673. {
  674. y = y * 10 + (p_cli->p_ctx->temp_buff[buff_idx++] - '0');
  675. if (buff_idx >= NRF_CLI_CMD_BUFF_SIZE)
  676. {
  677. return NRF_ERROR_DATA_SIZE;
  678. }
  679. }
  680. if (++buff_idx >= NRF_CLI_CMD_BUFF_SIZE)
  681. {
  682. return NRF_ERROR_DATA_SIZE;
  683. }
  684. while (p_cli->p_ctx->temp_buff[buff_idx] != '\0')
  685. {
  686. x = x * 10 + (p_cli->p_ctx->temp_buff[buff_idx++] - '0');
  687. if (buff_idx >= NRF_CLI_CMD_BUFF_SIZE)
  688. {
  689. return NRF_ERROR_DATA_SIZE;
  690. }
  691. }
  692. /* horizontal cursor position */
  693. if (x > NRF_CLI_MAX_TERMINAL_SIZE)
  694. {
  695. p_cli->p_ctx->vt100_ctx.cons.cur_x = NRF_CLI_MAX_TERMINAL_SIZE;
  696. }
  697. else
  698. {
  699. p_cli->p_ctx->vt100_ctx.cons.cur_x = (nrf_cli_cmd_len_t)x;
  700. }
  701. /* vertical cursor position */
  702. if (y > NRF_CLI_MAX_TERMINAL_SIZE)
  703. {
  704. p_cli->p_ctx->vt100_ctx.cons.cur_y = NRF_CLI_MAX_TERMINAL_SIZE;
  705. }
  706. else
  707. {
  708. p_cli->p_ctx->vt100_ctx.cons.cur_y = (nrf_cli_cmd_len_t)y;
  709. }
  710. p_cli->p_ctx->temp_buff[0] = 0;
  711. return NRF_SUCCESS;
  712. }
  713. else
  714. {
  715. p_cli->p_ctx->temp_buff[buff_idx] = c;
  716. }
  717. if (++buff_idx > NRF_CLI_CURSOR_POSITION_BUFFER - 1)
  718. {
  719. p_cli->p_ctx->temp_buff[0] = 0;
  720. /* data_buf[NRF_CLI_CURSOR_POSITION_BUFFER - 1] is reserved for '\0' */
  721. return NRF_ERROR_NO_MEM;
  722. }
  723. } while (cnt > 0);
  724. }
  725. return NRF_ERROR_TIMEOUT;
  726. }
  727. /* Function gets terminal width and height. */
  728. static ret_code_t terminal_size_get(nrf_cli_t const * p_cli,
  729. nrf_cli_cmd_len_t * p_length,
  730. nrf_cli_cmd_len_t * p_height)
  731. {
  732. ASSERT(p_length);
  733. ASSERT(p_height);
  734. uint16_t x;
  735. uint16_t y;
  736. if (cursor_position_get(p_cli) == NRF_SUCCESS)
  737. {
  738. x = p_cli->p_ctx->vt100_ctx.cons.cur_x;
  739. y = p_cli->p_ctx->vt100_ctx.cons.cur_y;
  740. /* assumption: terminal widht and height < 999 */
  741. cursor_right_move(p_cli, NRF_CLI_MAX_TERMINAL_SIZE); /* move to last column */
  742. cursor_down_move(p_cli, NRF_CLI_MAX_TERMINAL_SIZE); /* move to last row */
  743. }
  744. else
  745. {
  746. return NRF_ERROR_NOT_SUPPORTED;
  747. }
  748. if (cursor_position_get(p_cli) == NRF_SUCCESS)
  749. {
  750. *p_length = p_cli->p_ctx->vt100_ctx.cons.cur_x;
  751. *p_height = p_cli->p_ctx->vt100_ctx.cons.cur_y;
  752. cursor_left_move(p_cli, *p_length - x);
  753. cursor_up_move(p_cli, *p_height - y);
  754. return NRF_SUCCESS;
  755. }
  756. return NRF_ERROR_NOT_SUPPORTED;
  757. }
  758. #endif // NRF_MODULE_ENABLED(NRF_CLI_BUILD_IN_CMDS)
  759. #if NRF_MODULE_ENABLED(NRF_CLI_VT100_COLORS)
  760. static void vt100_color_set(nrf_cli_t const * p_cli, nrf_cli_vt100_color_t color)
  761. {
  762. if (color != NRF_CLI_DEFAULT)
  763. {
  764. if (p_cli->p_ctx->vt100_ctx.col.col == color)
  765. {
  766. return;
  767. }
  768. uint8_t cmd[] = NRF_CLI_VT100_COLOR(color - 1);
  769. p_cli->p_ctx->vt100_ctx.col.col = color;
  770. nrf_fprintf(p_cli->p_fprintf_ctx, "%s", cmd);
  771. }
  772. else
  773. {
  774. static uint8_t const cmd[] = NRF_CLI_VT100_MODESOFF;
  775. p_cli->p_ctx->vt100_ctx.col.col = color;
  776. nrf_fprintf(p_cli->p_fprintf_ctx, "%s", cmd);
  777. }
  778. }
  779. static void vt100_bgcolor_set(nrf_cli_t const * p_cli, nrf_cli_vt100_color_t bgcolor)
  780. {
  781. if (bgcolor != NRF_CLI_DEFAULT)
  782. {
  783. if (p_cli->p_ctx->vt100_ctx.col.bgcol == bgcolor)
  784. {
  785. return;
  786. }
  787. /* -1 because default value is first in enum */
  788. uint8_t cmd[] = NRF_CLI_VT100_BGCOLOR(bgcolor - 1);
  789. p_cli->p_ctx->vt100_ctx.col.bgcol = bgcolor;
  790. nrf_fprintf(p_cli->p_fprintf_ctx, "%s", cmd);
  791. }
  792. }
  793. static inline void vt100_colors_store(nrf_cli_t const * p_cli,
  794. nrf_cli_vt100_colors_t * p_color)
  795. {
  796. memcpy(p_color, &p_cli->p_ctx->vt100_ctx.col, sizeof(nrf_cli_vt100_colors_t));
  797. }
  798. static void vt100_colors_restore(nrf_cli_t const * p_cli,
  799. nrf_cli_vt100_colors_t const * p_color)
  800. {
  801. vt100_color_set(p_cli, p_color->col);
  802. vt100_bgcolor_set(p_cli, p_color->bgcol);
  803. }
  804. #endif // NRF_MODULE_ENABLED(NRF_CLI_VT100_COLORS)
  805. static void left_arrow_handle(nrf_cli_t const * p_cli)
  806. {
  807. nrf_cli_multiline_cons_t const * p_cons = multiline_console_data_check(p_cli);
  808. if ((p_cons->cur_x == p_cons->name_len + NRF_CLI_INITIAL_CURS_POS) &&
  809. (p_cons->cur_y == NRF_CLI_INITIAL_CURS_POS))
  810. {
  811. return; /* nothing to handle because cursor is in start position */
  812. }
  813. if (p_cons->cur_x == NRF_CLI_INITIAL_CURS_POS)
  814. { /* go to previous line */
  815. cursor_up_move(p_cli, 1);
  816. cursor_right_move(p_cli, p_cons->terminal_wid);
  817. --p_cli->p_ctx->cmd_buff_pos;
  818. }
  819. else
  820. {
  821. cursor_left_move(p_cli, 1);
  822. --p_cli->p_ctx->cmd_buff_pos;
  823. }
  824. }
  825. static void right_arrow_handle(nrf_cli_t const * p_cli)
  826. {
  827. nrf_cli_multiline_cons_t const * p_cons = multiline_console_data_check(p_cli);
  828. if ((p_cons->cur_x == p_cons->cur_x_end) &&
  829. (p_cons->cur_y == p_cons->cur_y_end))
  830. {
  831. return; /* nothing to handle because cursor is in start position */
  832. }
  833. if (p_cons->cur_x == p_cons->terminal_wid) /* go to next line */
  834. {
  835. cursor_down_move(p_cli, 1);
  836. cursor_left_move(p_cli, p_cons->terminal_wid);
  837. ++p_cli->p_ctx->cmd_buff_pos;
  838. }
  839. else
  840. {
  841. cursor_right_move(p_cli, 1);
  842. ++p_cli->p_ctx->cmd_buff_pos;
  843. }
  844. }
  845. static inline void char_insert_echo_off(nrf_cli_t const * p_cli, char data)
  846. {
  847. if (p_cli->p_ctx->cmd_buff_len >= (NRF_CLI_CMD_BUFF_SIZE - 1))
  848. {
  849. return;
  850. }
  851. p_cli->p_ctx->cmd_buff[p_cli->p_ctx->cmd_buff_pos++] = data;
  852. p_cli->p_ctx->cmd_buff[p_cli->p_ctx->cmd_buff_pos] = '\0';
  853. ++p_cli->p_ctx->cmd_buff_len;
  854. }
  855. static void char_insert(nrf_cli_t const * p_cli, char data)
  856. {
  857. nrf_cli_cmd_len_t diff;
  858. bool ins_mode = (bool)p_cli->p_ctx->internal.flag.insert_mode;
  859. diff = p_cli->p_ctx->cmd_buff_len - p_cli->p_ctx->cmd_buff_pos;
  860. if (!ins_mode)
  861. {
  862. if (p_cli->p_ctx->cmd_buff_len >= (NRF_CLI_CMD_BUFF_SIZE - 1))
  863. {
  864. return;
  865. }
  866. if (diff > 0)
  867. {
  868. memmove(&p_cli->p_ctx->cmd_buff[p_cli->p_ctx->cmd_buff_pos + 1],
  869. &p_cli->p_ctx->cmd_buff[p_cli->p_ctx->cmd_buff_pos],
  870. diff);
  871. }
  872. }
  873. else
  874. {
  875. if ((p_cli->p_ctx->cmd_buff_len >= (NRF_CLI_CMD_BUFF_SIZE - 1)) &&
  876. (diff == 0))
  877. {
  878. /* If cmd buffer is full, it is possible to replace chars but adding new
  879. is not allowed. */
  880. return;
  881. }
  882. }
  883. p_cli->p_ctx->cmd_buff[p_cli->p_ctx->cmd_buff_pos] = data;
  884. if (!ins_mode)
  885. {
  886. p_cli->p_ctx->cmd_buff[++p_cli->p_ctx->cmd_buff_len] = '\0';
  887. }
  888. if (diff > 0)
  889. {
  890. nrf_cli_multiline_cons_t const * p_cons = multiline_console_data_check(p_cli);
  891. bool last_line = p_cons->cur_y == p_cons->cur_y_end ? true : false;
  892. /* Below if-else statement is to minimize esc codes transmission. */
  893. if (last_line)
  894. {
  895. nrf_cli_fprintf(p_cli,
  896. NRF_CLI_NORMAL,
  897. "%s",
  898. &p_cli->p_ctx->cmd_buff[p_cli->p_ctx->cmd_buff_pos]);
  899. /* Move cursor one position left less in case of insert mode. */
  900. cursor_left_move(p_cli, diff - ins_mode);
  901. }
  902. else
  903. {
  904. /* Save the current cursor position in order to get back after fprintf function. */
  905. cli_cursor_save(p_cli);
  906. nrf_cli_fprintf(p_cli,
  907. NRF_CLI_NORMAL,
  908. "%s",
  909. &p_cli->p_ctx->cmd_buff[p_cli->p_ctx->cmd_buff_pos]);
  910. cli_cursor_restore(p_cli);
  911. /* Move cursor right by one position to edit the next character. */
  912. cursor_right_move(p_cli, 1);
  913. }
  914. }
  915. else
  916. {
  917. /* New char appended at the end of buffer. */
  918. if (ins_mode)
  919. {
  920. p_cli->p_ctx->cmd_buff[++p_cli->p_ctx->cmd_buff_len] = '\0';
  921. }
  922. cli_putc(p_cli, data);
  923. }
  924. /* Incrementation needs to be executed before invoking function: cursor_in_empty_line. */
  925. ++p_cli->p_ctx->cmd_buff_pos;
  926. /* Forcing terminal to switch to a new line if the command is too long. */
  927. if (cursor_in_empty_line(p_cli))
  928. {
  929. cursor_next_line_move(p_cli);
  930. return;
  931. }
  932. if (full_line_cmd(p_cli))
  933. {
  934. nrf_cli_multiline_cons_t const * p_cons = multiline_console_data_check(p_cli);
  935. /* The code below will force the terminal to scroll one line down when the currently entered command
  936. * reaches lower right corner of the terminal screen. */
  937. cursor_down_move(p_cli, p_cons->cur_y_end - p_cons->cur_y - 1);
  938. cursor_next_line_move(p_cli);
  939. cursor_up_move(p_cli, p_cons->cur_y_end - p_cons->cur_y);
  940. cursor_right_move(p_cli, p_cons->cur_x - 1);
  941. return;
  942. }
  943. }
  944. static void char_backspace(nrf_cli_t const * p_cli)
  945. {
  946. nrf_cli_cmd_len_t diff;
  947. if ((p_cli->p_ctx->cmd_buff_len == 0) || (p_cli->p_ctx->cmd_buff_pos == 0))
  948. {
  949. return;
  950. }
  951. diff = p_cli->p_ctx->cmd_buff_len - p_cli->p_ctx->cmd_buff_pos;
  952. memmove(&p_cli->p_ctx->cmd_buff[p_cli->p_ctx->cmd_buff_pos - 1],
  953. &p_cli->p_ctx->cmd_buff[p_cli->p_ctx->cmd_buff_pos],
  954. diff + 1);
  955. --p_cli->p_ctx->cmd_buff_pos;
  956. --p_cli->p_ctx->cmd_buff_len;
  957. if (diff > 0)
  958. {
  959. cli_putc(p_cli, NRF_CLI_VT100_ASCII_BSPACE);
  960. nrf_cli_multiline_cons_t const * p_cons = multiline_console_data_check(p_cli);
  961. bool last_line = p_cons->cur_y == p_cons->cur_y_end ? true : false;
  962. if (last_line)
  963. {
  964. nrf_cli_fprintf(p_cli,
  965. NRF_CLI_NORMAL,
  966. "%s",
  967. &p_cli->p_ctx->cmd_buff[p_cli->p_ctx->cmd_buff_pos]);
  968. cli_clear_eos(p_cli);
  969. cursor_left_move(p_cli, diff);
  970. }
  971. else
  972. {
  973. /* If cursor is not in last cmd line, its position needs to be saved by
  974. * VT100 command. */
  975. cli_cursor_save(p_cli);
  976. cli_clear_eos(p_cli);
  977. nrf_cli_fprintf(p_cli,
  978. NRF_CLI_NORMAL,
  979. "%s",
  980. &p_cli->p_ctx->cmd_buff[p_cli->p_ctx->cmd_buff_pos]);
  981. cli_cursor_restore(p_cli);
  982. }
  983. }
  984. else
  985. {
  986. static char const cmd_bspace[] = {
  987. NRF_CLI_VT100_ASCII_BSPACE, ' ', NRF_CLI_VT100_ASCII_BSPACE, '\0'};
  988. nrf_fprintf(p_cli->p_fprintf_ctx, "%s", cmd_bspace);
  989. }
  990. }
  991. static void char_delete(nrf_cli_t const * p_cli)
  992. {
  993. nrf_cli_cmd_len_t diff;
  994. diff = p_cli->p_ctx->cmd_buff_len - p_cli->p_ctx->cmd_buff_pos;
  995. if (diff == 0)
  996. {
  997. return;
  998. }
  999. memmove(&p_cli->p_ctx->cmd_buff[p_cli->p_ctx->cmd_buff_pos],
  1000. &p_cli->p_ctx->cmd_buff[p_cli->p_ctx->cmd_buff_pos + 1],
  1001. diff);
  1002. --p_cli->p_ctx->cmd_buff_len;
  1003. nrf_cli_multiline_cons_t const * p_cons = multiline_console_data_check(p_cli);
  1004. bool last_line = p_cons->cur_y == p_cons->cur_y_end ? true : false;
  1005. /* If last line of command is edited, there is no need for saving cursor position. */
  1006. if (last_line)
  1007. {
  1008. nrf_cli_fprintf(p_cli,
  1009. NRF_CLI_NORMAL,
  1010. "%s",
  1011. &p_cli->p_ctx->cmd_buff[p_cli->p_ctx->cmd_buff_pos]);
  1012. NRF_CLI_VT100_CMD(p_cli, NRF_CLI_VT100_CLEAREOL);
  1013. cursor_left_move(p_cli, --diff);
  1014. }
  1015. else
  1016. {
  1017. cli_cursor_save(p_cli);
  1018. cli_clear_eos(p_cli);
  1019. nrf_cli_fprintf(p_cli,
  1020. NRF_CLI_NORMAL,
  1021. "%s",
  1022. &p_cli->p_ctx->cmd_buff[p_cli->p_ctx->cmd_buff_pos]);
  1023. cli_cursor_restore(p_cli);
  1024. }
  1025. }
  1026. static char make_argv(size_t * p_argc, char ** pp_argv, char * p_cmd, uint8_t max_argc)
  1027. {
  1028. char c;
  1029. char quote = 0;
  1030. *p_argc = 0;
  1031. do
  1032. {
  1033. c = *p_cmd;
  1034. if (c == '\0')
  1035. {
  1036. break;
  1037. }
  1038. if (isspace((int)c))
  1039. {
  1040. *p_cmd++ = '\0';
  1041. continue;
  1042. }
  1043. pp_argv[(*p_argc)++] = p_cmd;
  1044. quote = 0;
  1045. while (1)
  1046. {
  1047. c = *p_cmd;
  1048. if (c == '\0')
  1049. {
  1050. break;
  1051. }
  1052. if (!quote)
  1053. {
  1054. switch (c)
  1055. {
  1056. case '\\':
  1057. memmove(p_cmd, p_cmd + 1, cli_strlen(p_cmd));
  1058. p_cmd += 1;
  1059. continue;
  1060. case '\'':
  1061. case '\"':
  1062. memmove(p_cmd, p_cmd + 1, cli_strlen(p_cmd));
  1063. quote = c;
  1064. continue;
  1065. default:
  1066. break;
  1067. }
  1068. }
  1069. if (quote == c)
  1070. {
  1071. memmove(p_cmd, p_cmd + 1, cli_strlen(p_cmd));
  1072. quote = 0;
  1073. continue;
  1074. }
  1075. if (quote && c == '\\')
  1076. {
  1077. char t = *(p_cmd + 1);
  1078. if (t == quote)
  1079. {
  1080. memmove(p_cmd, p_cmd + 1, cli_strlen(p_cmd));
  1081. p_cmd += 1;
  1082. continue;
  1083. }
  1084. if (t == '0')
  1085. {
  1086. uint8_t i;
  1087. uint8_t v = 0;
  1088. for (i = 2; i < (2 + 3); i++)
  1089. {
  1090. t = *(p_cmd + i);
  1091. if (t >= '0' && t <= '7')
  1092. {
  1093. v = (v << 3) | (t - '0');
  1094. }
  1095. else
  1096. {
  1097. break;
  1098. }
  1099. }
  1100. if (i > 2)
  1101. {
  1102. memmove(p_cmd, p_cmd + (i - 1), cli_strlen(p_cmd) - (i - 2));
  1103. *p_cmd++ = v;
  1104. continue;
  1105. }
  1106. }
  1107. if (t == 'x')
  1108. {
  1109. uint8_t i;
  1110. uint8_t v = 0;
  1111. for (i = 2; i < (2 + 2); i++)
  1112. {
  1113. t = *(p_cmd + i);
  1114. if (t >= '0' && t <= '9')
  1115. {
  1116. v = (v << 4) | (t - '0');
  1117. }
  1118. else if (t >= 'a' && t <= 'f')
  1119. {
  1120. v = (v << 4) | (t - 'a' + 10);
  1121. }
  1122. else if (t >= 'A' && t <= 'F')
  1123. {
  1124. v = (v << 4) | (t - 'A' + 10);
  1125. }
  1126. else
  1127. {
  1128. break;
  1129. }
  1130. }
  1131. if (i > 2)
  1132. {
  1133. memmove(p_cmd, p_cmd + (i - 1), cli_strlen(p_cmd) - (i - 2));
  1134. *p_cmd++ = v;
  1135. continue;
  1136. }
  1137. }
  1138. }
  1139. if (!quote && isspace((int)c))
  1140. {
  1141. break;
  1142. }
  1143. p_cmd += 1;
  1144. }
  1145. } while (*p_argc < max_argc);
  1146. ASSERT(*p_argc <= NRF_CLI_ARGC_MAX);
  1147. pp_argv[*p_argc] = 0;
  1148. return quote;
  1149. }
  1150. static void cli_state_set(nrf_cli_t const * p_cli, nrf_cli_state_t state)
  1151. {
  1152. p_cli->p_ctx->state = state;
  1153. if (state == NRF_CLI_STATE_ACTIVE)
  1154. {
  1155. cli_cmd_buffer_clear(p_cli);
  1156. nrf_cli_fprintf(p_cli, NRF_CLI_INFO, "%s", p_cli->p_name);
  1157. }
  1158. }
  1159. #if NRF_MODULE_ENABLED(NRF_CLI_HISTORY)
  1160. static inline void history_mode_exit(nrf_cli_t const * p_cli)
  1161. {
  1162. p_cli->p_ctx->p_cmd_list_element = NULL;
  1163. }
  1164. static void history_handle(nrf_cli_t const * p_cli, bool up)
  1165. {
  1166. nrf_cli_memobj_header_t header = {
  1167. .p_prev = NULL,
  1168. .p_next = NULL,
  1169. .cmd_len = 0
  1170. };
  1171. nrf_cli_cmd_len_t current_cmd_len;
  1172. bool skip = false;
  1173. if (!up) /* button down */
  1174. {
  1175. if (p_cli->p_ctx->p_cmd_list_element == NULL)
  1176. {
  1177. return;
  1178. }
  1179. cursor_home_position_move(p_cli);
  1180. nrf_memobj_read(p_cli->p_ctx->p_cmd_list_element,
  1181. &header,
  1182. NRF_CLI_HISTORY_HEADER_SIZE,
  1183. 0);
  1184. p_cli->p_ctx->p_cmd_list_element = header.p_next;
  1185. current_cmd_len = p_cli->p_ctx->cmd_buff_len;
  1186. if (p_cli->p_ctx->p_cmd_list_element == NULL)
  1187. {
  1188. if (cli_strlen(p_cli->p_ctx->temp_buff) > 0)
  1189. {
  1190. strcpy(p_cli->p_ctx->cmd_buff, p_cli->p_ctx->temp_buff);
  1191. }
  1192. else
  1193. {
  1194. p_cli->p_ctx->cmd_buff[0] = '\0';
  1195. }
  1196. header.cmd_len = cli_strlen(p_cli->p_ctx->cmd_buff);
  1197. skip = true;
  1198. }
  1199. }
  1200. else /* button up */
  1201. {
  1202. if ((p_cli->p_ctx->p_cmd_list_element == p_cli->p_ctx->p_cmd_list_tail) ||
  1203. (p_cli->p_ctx->p_cmd_list_head == NULL))
  1204. {
  1205. /* Nothing to display. */
  1206. return;
  1207. }
  1208. cursor_home_position_move(p_cli);
  1209. if (p_cli->p_ctx->p_cmd_list_element == NULL)
  1210. {
  1211. current_cmd_len = cli_strlen(p_cli->p_ctx->cmd_buff);
  1212. p_cli->p_ctx->p_cmd_list_element = p_cli->p_ctx->p_cmd_list_head;
  1213. /* Save the currently entered and not executed command. */
  1214. if (current_cmd_len > 0)
  1215. {
  1216. strcpy(p_cli->p_ctx->temp_buff, p_cli->p_ctx->cmd_buff);
  1217. }
  1218. else
  1219. {
  1220. p_cli->p_ctx->temp_buff[0] = '\0';
  1221. }
  1222. }
  1223. else
  1224. {
  1225. nrf_memobj_read(p_cli->p_ctx->p_cmd_list_element,
  1226. &header,
  1227. NRF_CLI_HISTORY_HEADER_SIZE,
  1228. 0);
  1229. current_cmd_len = header.cmd_len;
  1230. p_cli->p_ctx->p_cmd_list_element = header.p_prev;
  1231. }
  1232. }
  1233. if (!skip)
  1234. {
  1235. nrf_memobj_read(p_cli->p_ctx->p_cmd_list_element,
  1236. &header,
  1237. NRF_CLI_HISTORY_HEADER_SIZE,
  1238. 0);
  1239. nrf_memobj_read(p_cli->p_ctx->p_cmd_list_element,
  1240. p_cli->p_ctx->cmd_buff,
  1241. header.cmd_len + 1, /* +1 for '\0' */
  1242. NRF_CLI_HISTORY_HEADER_SIZE);
  1243. }
  1244. p_cli->p_ctx->cmd_buff_pos = header.cmd_len;
  1245. p_cli->p_ctx->cmd_buff_len = header.cmd_len;
  1246. if (current_cmd_len > header.cmd_len)
  1247. {
  1248. cli_clear_eos(p_cli);
  1249. }
  1250. nrf_cli_fprintf(p_cli, NRF_CLI_NORMAL, "%s", p_cli->p_ctx->cmd_buff);
  1251. if (cursor_in_empty_line(p_cli) || full_line_cmd(p_cli))
  1252. {
  1253. cursor_next_line_move(p_cli);
  1254. }
  1255. }
  1256. static void history_list_element_add(nrf_cli_t const * p_cli, nrf_memobj_t * p_memobj)
  1257. {
  1258. ASSERT(p_memobj != NULL);
  1259. nrf_cli_memobj_header_t header;
  1260. if (p_cli->p_ctx->p_cmd_list_head == NULL)
  1261. {
  1262. p_cli->p_ctx->p_cmd_list_head = p_memobj;
  1263. p_cli->p_ctx->p_cmd_list_tail = p_memobj;
  1264. header.p_prev = NULL;
  1265. header.p_next = NULL;
  1266. header.cmd_len = p_cli->p_ctx->cmd_buff_len;
  1267. }
  1268. else
  1269. {
  1270. nrf_memobj_read(p_cli->p_ctx->p_cmd_list_head,
  1271. &header,
  1272. NRF_CLI_HISTORY_HEADER_SIZE,
  1273. 0);
  1274. header.p_next = p_memobj;
  1275. nrf_memobj_write(p_cli->p_ctx->p_cmd_list_head,
  1276. &header,
  1277. NRF_CLI_HISTORY_HEADER_SIZE,
  1278. 0);
  1279. header.p_next = NULL;
  1280. header.p_prev = p_cli->p_ctx->p_cmd_list_head;
  1281. header.cmd_len = p_cli->p_ctx->cmd_buff_len;
  1282. p_cli->p_ctx->p_cmd_list_head = p_memobj;
  1283. }
  1284. nrf_memobj_write(p_memobj,
  1285. &header,
  1286. NRF_CLI_HISTORY_HEADER_SIZE,
  1287. 0);
  1288. nrf_memobj_write(p_memobj,
  1289. p_cli->p_ctx->cmd_buff,
  1290. p_cli->p_ctx->cmd_buff_len + 1, /* +1 for '\0' */
  1291. NRF_CLI_HISTORY_HEADER_SIZE);
  1292. }
  1293. static void history_list_element_oldest_remove(nrf_cli_t const * p_cli)
  1294. {
  1295. if (p_cli->p_ctx->p_cmd_list_tail == NULL)
  1296. {
  1297. return; // nothing to do
  1298. }
  1299. nrf_cli_memobj_header_t header;
  1300. nrf_memobj_t * p_memobj = p_cli->p_ctx->p_cmd_list_tail;
  1301. nrf_memobj_read(p_memobj,
  1302. &header,
  1303. NRF_CLI_HISTORY_HEADER_SIZE,
  1304. 0);
  1305. p_cli->p_ctx->p_cmd_list_tail = header.p_next;
  1306. memset(&header, 0, sizeof(nrf_cli_memobj_header_t));
  1307. nrf_memobj_write(p_memobj, &header, NRF_CLI_HISTORY_HEADER_SIZE, 0);
  1308. nrf_memobj_free(p_memobj);
  1309. /* Checking if memory objects list is empty. */
  1310. if (p_cli->p_ctx->p_cmd_list_tail == NULL)
  1311. {
  1312. p_cli->p_ctx->p_cmd_list_head = NULL;
  1313. return;
  1314. }
  1315. nrf_memobj_read(p_cli->p_ctx->p_cmd_list_tail,
  1316. &header,
  1317. NRF_CLI_HISTORY_HEADER_SIZE,
  1318. 0);
  1319. header.p_prev = NULL;
  1320. nrf_memobj_write(p_cli->p_ctx->p_cmd_list_tail, &header, NRF_CLI_HISTORY_HEADER_SIZE, 0);
  1321. }
  1322. static void history_list_free_memory(nrf_cli_t const * p_cli)
  1323. {
  1324. while (p_cli->p_ctx->p_cmd_list_tail != NULL)
  1325. {
  1326. history_list_element_oldest_remove(p_cli);
  1327. }
  1328. }
  1329. static void history_save(nrf_cli_t const * p_cli)
  1330. {
  1331. nrf_cli_cmd_len_t cmd_new_len = cli_strlen(p_cli->p_ctx->cmd_buff);
  1332. history_mode_exit(p_cli);
  1333. if (cmd_new_len == 0)
  1334. {
  1335. return;
  1336. }
  1337. /* Checking if newly entered command is not duplicated with previous one. */
  1338. if (p_cli->p_ctx->p_cmd_list_head != NULL)
  1339. {
  1340. nrf_cli_memobj_header_t header;
  1341. nrf_memobj_read(p_cli->p_ctx->p_cmd_list_head,
  1342. &header,
  1343. NRF_CLI_HISTORY_HEADER_SIZE,
  1344. 0);
  1345. if (cmd_new_len == header.cmd_len)
  1346. {
  1347. nrf_memobj_read(p_cli->p_ctx->p_cmd_list_head,
  1348. p_cli->p_ctx->temp_buff,
  1349. header.cmd_len + 1, /* +1 for '\0' */
  1350. NRF_CLI_HISTORY_HEADER_SIZE);
  1351. if (strcmp(p_cli->p_ctx->cmd_buff, p_cli->p_ctx->temp_buff) == 0)
  1352. {
  1353. /* Duplicated command, nothing to save. */
  1354. p_cli->p_ctx->temp_buff[0] = '\0';
  1355. return;
  1356. }
  1357. p_cli->p_ctx->temp_buff[0] = '\0';
  1358. }
  1359. }
  1360. for (size_t idx = 0; idx < NRF_CLI_HISTORY_ELEMENT_COUNT; idx++)
  1361. {
  1362. nrf_memobj_t * p_memobj;
  1363. p_memobj = nrf_memobj_alloc(p_cli->p_cmd_hist_mempool,
  1364. ((size_t)NRF_CLI_HISTORY_HEADER_SIZE + cmd_new_len + 1));
  1365. if (p_memobj != NULL)
  1366. {
  1367. history_list_element_add(p_cli, p_memobj);
  1368. return;
  1369. }
  1370. else
  1371. {
  1372. history_list_element_oldest_remove(p_cli);
  1373. }
  1374. }
  1375. return;
  1376. }
  1377. #endif // NRF_MODULE_ENABLED(NRF_CLI_HISTORY)
  1378. /* Function checks how many identical characters have two strings starting from the first character. */
  1379. static nrf_cli_cmd_len_t str_similarity_check(char const * str_a, char const * str_b)
  1380. {
  1381. nrf_cli_cmd_len_t cnt = 0;
  1382. while (str_a[cnt] != '\0')
  1383. {
  1384. if (str_a[cnt] != str_b[cnt])
  1385. {
  1386. return cnt;
  1387. }
  1388. if (++cnt == 0)
  1389. {
  1390. return --cnt; /* too long strings */
  1391. }
  1392. }
  1393. return cnt;
  1394. }
  1395. static void completion_insert(nrf_cli_t const * p_cli,
  1396. char const * p_compl,
  1397. nrf_cli_cmd_len_t compl_len)
  1398. {
  1399. ASSERT (p_compl);
  1400. nrf_cli_cmd_len_t diff = p_cli->p_ctx->cmd_buff_len - p_cli->p_ctx->cmd_buff_pos;
  1401. if ((p_cli->p_ctx->cmd_buff_len + compl_len > NRF_CLI_CMD_BUFF_SIZE - 1) ||
  1402. (compl_len == 0))
  1403. {
  1404. return;
  1405. }
  1406. /* Make space for completion. */
  1407. memmove(&p_cli->p_ctx->cmd_buff[p_cli->p_ctx->cmd_buff_pos + compl_len],
  1408. &p_cli->p_ctx->cmd_buff[p_cli->p_ctx->cmd_buff_pos],
  1409. diff + 1); /* + 1 for '\0' */
  1410. /* Insert completion. */
  1411. memmove(&p_cli->p_ctx->cmd_buff[p_cli->p_ctx->cmd_buff_pos],
  1412. p_compl,
  1413. compl_len);
  1414. p_cli->p_ctx->cmd_buff_len = cli_strlen(p_cli->p_ctx->cmd_buff);
  1415. nrf_cli_fprintf(p_cli,
  1416. NRF_CLI_NORMAL,
  1417. "%s",
  1418. &p_cli->p_ctx->cmd_buff[p_cli->p_ctx->cmd_buff_pos]);
  1419. p_cli->p_ctx->cmd_buff_pos += compl_len;
  1420. if (cursor_in_empty_line(p_cli) || full_line_cmd(p_cli))
  1421. {
  1422. cursor_next_line_move(p_cli);
  1423. }
  1424. if (diff > 0)
  1425. {
  1426. cursor_position_synchronize(p_cli);
  1427. }
  1428. }
  1429. static void option_print(nrf_cli_t const * p_cli,
  1430. char const * p_option,
  1431. nrf_cli_cmd_len_t longest_option)
  1432. {
  1433. static char const * tab = " ";
  1434. /* Function initialization has been requested. */
  1435. if (p_option == NULL)
  1436. {
  1437. p_cli->p_ctx->vt100_ctx.printed_cmd = 0;
  1438. return;
  1439. }
  1440. longest_option += cli_strlen(tab);
  1441. nrf_cli_cmd_len_t columns =
  1442. (p_cli->p_ctx->vt100_ctx.cons.terminal_wid - cli_strlen(tab)) / longest_option;
  1443. nrf_cli_cmd_len_t diff = longest_option - cli_strlen(p_option);
  1444. if (p_cli->p_ctx->vt100_ctx.printed_cmd++ % columns == 0)
  1445. {
  1446. nrf_cli_fprintf(p_cli, NRF_CLI_OPTION, "\r\n%s%s", tab, p_option);
  1447. }
  1448. else
  1449. {
  1450. nrf_cli_fprintf(p_cli, NRF_CLI_OPTION, "%s", p_option);
  1451. }
  1452. cursor_right_move(p_cli, diff);
  1453. }
  1454. static void cli_tab_handle(nrf_cli_t const * p_cli)
  1455. {
  1456. size_t cmd_idx;
  1457. size_t cmd_last = 0;
  1458. size_t cmd_first = 0;
  1459. size_t argc;
  1460. char * argv[NRF_CLI_ARGC_MAX + 1]; /* +1 reserved for NULL in function make_argv */
  1461. nrf_cli_cmd_len_t cmd_lvl = NRF_CLI_CMD_ROOT_LVL;
  1462. nrf_cli_cmd_len_t cmd_longest = 0; /* longest matching command */
  1463. /* Calculating the longest possible completion length. -1 for '\0'. */
  1464. nrf_cli_cmd_len_t compl_len = (NRF_CLI_CMD_BUFF_SIZE - 1) - p_cli->p_ctx->cmd_buff_len;
  1465. if (compl_len == 0)
  1466. {
  1467. return;
  1468. }
  1469. /* Copy command from its beginning to cursor position. */
  1470. memcpy(p_cli->p_ctx->temp_buff,
  1471. p_cli->p_ctx->cmd_buff,
  1472. p_cli->p_ctx->cmd_buff_pos);
  1473. p_cli->p_ctx->temp_buff[p_cli->p_ctx->cmd_buff_pos] = '\0';
  1474. /* Check if the current cursor position points to the 'space' character. */
  1475. bool space = isspace((int)p_cli->p_ctx->cmd_buff[p_cli->p_ctx->cmd_buff_pos - 1]);
  1476. #if NRF_MODULE_ENABLED(NRF_CLI_HISTORY)
  1477. /* If the Tab key is pressed, "history mode" must be terminated because tab and history handlers
  1478. are sharing the same array: temp_buff. */
  1479. history_mode_exit(p_cli);
  1480. #endif
  1481. /* Create argument list. */
  1482. (void)make_argv(&argc,
  1483. &argv[0],
  1484. p_cli->p_ctx->temp_buff,
  1485. NRF_CLI_ARGC_MAX);
  1486. nrf_cli_cmd_len_t arg_len = cli_strlen(argv[cmd_lvl]);
  1487. /* Variable 'static_entry' is needed to handle dynamic commands. */
  1488. nrf_cli_static_entry_t static_entry;
  1489. nrf_cli_cmd_entry_t const * p_cmd = NULL;
  1490. nrf_cli_static_entry_t const * p_st_cmd = NULL;
  1491. nrf_cli_static_entry_t const * p_st_cmd_last = NULL;
  1492. do
  1493. {
  1494. if ((argc == 0) ||
  1495. (cmd_lvl >= argc - 1 + space))
  1496. {
  1497. if (space)
  1498. {
  1499. arg_len = 0;
  1500. }
  1501. else
  1502. {
  1503. arg_len = cli_strlen(argv[cmd_lvl]);
  1504. }
  1505. cmd_idx = 0;
  1506. while (1)
  1507. {
  1508. cmd_get(p_cmd, cmd_lvl, cmd_idx++, &p_st_cmd, &static_entry);
  1509. if (p_st_cmd == NULL)
  1510. {
  1511. /* No more commands available. */
  1512. break;
  1513. }
  1514. if (strncmp(argv[cmd_lvl], p_st_cmd->p_syntax, arg_len) != 0)
  1515. {
  1516. if (p_st_cmd_last != NULL)
  1517. {
  1518. /* No more matches will be found as commands are sorted alphabetically. */
  1519. break;
  1520. }
  1521. continue;
  1522. }
  1523. if (p_st_cmd_last == NULL)
  1524. {
  1525. cmd_first = cmd_idx - 1;
  1526. cmd_longest = cli_strlen(p_st_cmd->p_syntax);
  1527. if (compl_len > (cmd_longest - arg_len))
  1528. {
  1529. compl_len = cmd_longest - arg_len;
  1530. }
  1531. }
  1532. else
  1533. {
  1534. nrf_cli_cmd_len_t len = cli_strlen(p_st_cmd->p_syntax);
  1535. if (len > cmd_longest)
  1536. {
  1537. cmd_longest = len;
  1538. }
  1539. if (compl_len > 0) /* Checking if partial completion is possible */
  1540. {
  1541. nrf_cli_static_entry_t last_entry;
  1542. cmd_get(p_cmd, cmd_lvl, cmd_last, &p_st_cmd_last, &last_entry);
  1543. len = str_similarity_check(p_st_cmd->p_syntax + arg_len,
  1544. p_st_cmd_last->p_syntax + arg_len);
  1545. if (compl_len > len)
  1546. {
  1547. /* Determining the longest possible completion. */
  1548. compl_len = len;
  1549. }
  1550. }
  1551. }
  1552. cmd_last = cmd_idx - 1;
  1553. p_st_cmd_last = p_st_cmd;
  1554. if (cmd_idx == 0) /* Too many possibilities */
  1555. {
  1556. nrf_cli_fprintf(p_cli, NRF_CLI_WARNING, NRF_CLI_MSG_TAB_OVERFLOWED);
  1557. break;
  1558. }
  1559. }
  1560. }
  1561. else
  1562. {
  1563. cmd_idx = 0;
  1564. while (1)
  1565. {
  1566. cmd_get(p_cmd, cmd_lvl, cmd_idx++, &p_st_cmd, &static_entry);
  1567. if (cmd_idx == 0)
  1568. {
  1569. /* No match found and commands counter overflowed. */
  1570. nrf_cli_fprintf(p_cli, NRF_CLI_WARNING, NRF_CLI_MSG_TAB_OVERFLOWED);
  1571. return;
  1572. }
  1573. if (p_st_cmd == NULL) /* No more commands available */
  1574. {
  1575. return;
  1576. }
  1577. #if NRF_MODULE_ENABLED(NRF_CLI_WILDCARD)
  1578. /* Ignore wildcard character arguments if they are "standalone". Example:
  1579. 1. log enable info b*<tab> -> "b*" is treated as a command so no match found
  1580. 2. log enable info b* <tab> -> "b* " is ignored, <tab> will prompt all available
  1581. commands. */
  1582. if (wildcard_character_exist(argv[cmd_lvl]))
  1583. {
  1584. break;
  1585. }
  1586. #endif
  1587. /* Fuction "strcmp" is used because an exact match is required. */
  1588. if (strcmp(argv[cmd_lvl], p_st_cmd->p_syntax) == 0)
  1589. {
  1590. p_cmd = p_st_cmd->p_subcmd;
  1591. break;
  1592. }
  1593. }
  1594. }
  1595. if ((p_cmd == NULL) || (p_st_cmd == NULL))
  1596. {
  1597. break;
  1598. }
  1599. } while (++cmd_lvl < argc + space);
  1600. if (p_st_cmd_last == NULL)
  1601. {
  1602. /* No match found. */
  1603. return;
  1604. }
  1605. if (cmd_first == cmd_last) /* only one match found */
  1606. {
  1607. if (p_cmd->is_dynamic)
  1608. {
  1609. /* In case of dynamic entry, function cmd_get shall be called again for matching
  1610. * command index (cmd_last). It is because static_entry is most likely appended by
  1611. * not valid data.
  1612. */
  1613. cmd_get(p_cmd, cmd_lvl, cmd_last, &p_st_cmd_last, &static_entry);
  1614. }
  1615. if (cli_strlen(p_st_cmd_last->p_syntax) != arg_len) /* no exact match found */
  1616. {
  1617. completion_insert(p_cli, p_st_cmd_last->p_syntax + arg_len, compl_len);
  1618. }
  1619. /* Next character in the buffer is not 'space'. */
  1620. if (!isspace((int)p_cli->p_ctx->cmd_buff[p_cli->p_ctx->cmd_buff_pos]))
  1621. {
  1622. if (p_cli->p_ctx->internal.flag.insert_mode)
  1623. {
  1624. p_cli->p_ctx->internal.flag.insert_mode = 0;
  1625. char_insert(p_cli, ' ');
  1626. p_cli->p_ctx->internal.flag.insert_mode = 1;
  1627. }
  1628. else
  1629. {
  1630. char_insert(p_cli, ' ');
  1631. }
  1632. }
  1633. else
  1634. {
  1635. /* case:
  1636. | | -> cursor
  1637. cons_name $: valid_cmd valid_sub_cmd| |argument <tab>
  1638. */
  1639. cursor_position_increment(p_cli);
  1640. /* result:
  1641. cons_name $: valid_cmd valid_sub_cmd |a|rgument
  1642. */
  1643. }
  1644. return;
  1645. }
  1646. /* Printing all matching commands (options). */
  1647. option_print(p_cli, NRF_CLI_INIT_OPTION_PRINTER, cmd_longest);
  1648. for (cmd_idx = cmd_first; cmd_idx <= cmd_last; cmd_idx++)
  1649. {
  1650. cmd_get(p_cmd, cmd_lvl, cmd_idx, &p_st_cmd, &static_entry);
  1651. option_print(p_cli, p_st_cmd->p_syntax, cmd_longest);
  1652. }
  1653. nrf_cli_fprintf(p_cli, NRF_CLI_INFO, "\r\n%s", p_cli->p_name);
  1654. nrf_cli_fprintf(p_cli, NRF_CLI_NORMAL, "%s", p_cli->p_ctx->cmd_buff);
  1655. cursor_position_synchronize(p_cli);
  1656. completion_insert(p_cli, p_st_cmd_last->p_syntax + arg_len, compl_len);
  1657. }
  1658. #define NRF_CLI_ASCII_MAX_CHAR (127u)
  1659. static inline ret_code_t ascii_filter(char const data)
  1660. {
  1661. return (uint8_t)data > NRF_CLI_ASCII_MAX_CHAR ? NRF_ERROR_INVALID_DATA : NRF_SUCCESS;
  1662. }
  1663. static void cli_state_collect(nrf_cli_t const * p_cli)
  1664. {
  1665. size_t count = 0;
  1666. char data;
  1667. while (1)
  1668. {
  1669. cli_read(p_cli, &data, sizeof(data), &count);
  1670. if (count == 0)
  1671. {
  1672. return;
  1673. }
  1674. if (ascii_filter(data) != NRF_SUCCESS)
  1675. {
  1676. continue;
  1677. }
  1678. #if NRF_MODULE_ENABLED(NRF_PWR_MGMT)
  1679. nrf_pwr_mgmt_feed();
  1680. #endif
  1681. switch (p_cli->p_ctx->receive_state)
  1682. {
  1683. case NRF_CLI_RECEIVE_DEFAULT:
  1684. if (data == p_cli->newline_char)
  1685. {
  1686. if (p_cli->p_ctx->cmd_buff_len == 0)
  1687. {
  1688. history_mode_exit(p_cli);
  1689. cursor_next_line_move(p_cli);
  1690. }
  1691. else
  1692. {
  1693. /* Command execution */
  1694. cli_execute(p_cli);
  1695. }
  1696. cli_state_set(p_cli, NRF_CLI_STATE_ACTIVE);
  1697. return;
  1698. }
  1699. switch (data)
  1700. {
  1701. case NRF_CLI_VT100_ASCII_ESC: /* ESCAPE */
  1702. receive_state_change(p_cli, NRF_CLI_RECEIVE_ESC);
  1703. break;
  1704. case '\0':
  1705. break;
  1706. #if NRF_MODULE_ENABLED(NRF_CLI_METAKEYS)
  1707. case NRF_CLI_VT100_ASCII_CTRL_A: /* CTRL + A */
  1708. cursor_home_position_move(p_cli);
  1709. break;
  1710. case NRF_CLI_VT100_ASCII_CTRL_C: /* CTRL + C */
  1711. cursor_end_position_move(p_cli);
  1712. if (!cursor_in_empty_line(p_cli))
  1713. {
  1714. cursor_next_line_move(p_cli);
  1715. }
  1716. cli_state_set(p_cli, NRF_CLI_STATE_ACTIVE);
  1717. break;
  1718. case NRF_CLI_VT100_ASCII_CTRL_E: /* CTRL + E */
  1719. cursor_end_position_move(p_cli);
  1720. break;
  1721. case NRF_CLI_VT100_ASCII_CTRL_L: /* CTRL + L */
  1722. NRF_CLI_VT100_CMD(p_cli, NRF_CLI_VT100_CURSORHOME);
  1723. NRF_CLI_VT100_CMD(p_cli, NRF_CLI_VT100_CLEARSCREEN);
  1724. nrf_cli_fprintf(p_cli, NRF_CLI_INFO, "%s", p_cli->p_name);
  1725. if (cli_flag_echo_is_set(p_cli))
  1726. {
  1727. nrf_cli_fprintf(p_cli, NRF_CLI_NORMAL, "%s", p_cli->p_ctx->cmd_buff);
  1728. cursor_position_synchronize(p_cli);
  1729. }
  1730. break;
  1731. case NRF_CLI_VT100_ASCII_CTRL_U: /* CTRL + U */
  1732. cursor_home_position_move(p_cli);
  1733. cli_cmd_buffer_clear(p_cli);
  1734. cli_clear_eos(p_cli);
  1735. break;
  1736. case NRF_CLI_VT100_ASCII_CTRL_W: /* CTRL + W */
  1737. cli_cmd_word_remove(p_cli);
  1738. break;
  1739. #endif
  1740. case '\t': /* TAB */
  1741. if (cli_flag_echo_is_set(p_cli))
  1742. {
  1743. cli_tab_handle(p_cli);
  1744. }
  1745. break;
  1746. case NRF_CLI_VT100_ASCII_BSPACE: /* BACKSPACE */
  1747. if (cli_flag_echo_is_set(p_cli))
  1748. {
  1749. char_backspace(p_cli);
  1750. }
  1751. break;
  1752. case NRF_CLI_VT100_ASCII_DEL: /* DELETE */
  1753. if (cli_flag_echo_is_set(p_cli))
  1754. {
  1755. char_delete(p_cli);
  1756. }
  1757. break;
  1758. default:
  1759. if (isprint((int)data))
  1760. {
  1761. if (cli_flag_echo_is_set(p_cli))
  1762. {
  1763. char_insert(p_cli, data);
  1764. }
  1765. else
  1766. {
  1767. char_insert_echo_off(p_cli, data);
  1768. }
  1769. }
  1770. break;
  1771. }
  1772. break;
  1773. case NRF_CLI_RECEIVE_ESC:
  1774. if (data == '[')
  1775. {
  1776. receive_state_change(p_cli, NRF_CLI_RECEIVE_ESC_SEQ);
  1777. }
  1778. else
  1779. {
  1780. receive_state_change(p_cli, NRF_CLI_RECEIVE_DEFAULT);
  1781. }
  1782. break;
  1783. case NRF_CLI_RECEIVE_ESC_SEQ:
  1784. receive_state_change(p_cli, NRF_CLI_RECEIVE_DEFAULT);
  1785. if (!cli_flag_echo_is_set(p_cli))
  1786. {
  1787. return;
  1788. }
  1789. switch (data)
  1790. {
  1791. #if NRF_MODULE_ENABLED(NRF_CLI_HISTORY)
  1792. case 'A': /* UP arrow */
  1793. history_handle(p_cli, true);
  1794. break;
  1795. case 'B': /* DOWN arrow */
  1796. history_handle(p_cli, false);
  1797. break;
  1798. #endif
  1799. case 'C': /* RIGHT arrow */
  1800. right_arrow_handle(p_cli);
  1801. break;
  1802. case 'D': /* LEFT arrow */
  1803. left_arrow_handle(p_cli);
  1804. break;
  1805. case '4': /* END Button in ESC[n~ mode */
  1806. receive_state_change(p_cli, NRF_CLI_RECEIVE_TILDE_EXP);
  1807. /* fall through */
  1808. case 'F': /* END Button in VT100 mode */
  1809. cursor_end_position_move(p_cli);
  1810. break;
  1811. case '1': /* HOME Button in ESC[n~ mode */
  1812. receive_state_change(p_cli, NRF_CLI_RECEIVE_TILDE_EXP);
  1813. /* fall through */
  1814. case 'H': /* HOME Button in VT100 mode */
  1815. cursor_home_position_move(p_cli);
  1816. break;
  1817. case '2': /* INSERT Button in ESC[n~ mode */
  1818. receive_state_change(p_cli, NRF_CLI_RECEIVE_TILDE_EXP);
  1819. /* fall through */
  1820. case 'L': /* INSERT Button in VT100 mode */
  1821. p_cli->p_ctx->internal.flag.insert_mode ^= 1;
  1822. break;
  1823. case '3':/* DELETE Button in ESC[n~ mode */
  1824. receive_state_change(p_cli, NRF_CLI_RECEIVE_TILDE_EXP);
  1825. if (cli_flag_echo_is_set(p_cli))
  1826. {
  1827. char_delete(p_cli);
  1828. }
  1829. break;
  1830. default:
  1831. break;
  1832. }
  1833. break;
  1834. case NRF_CLI_RECEIVE_TILDE_EXP:
  1835. receive_state_change(p_cli, NRF_CLI_RECEIVE_DEFAULT);
  1836. break;
  1837. default:
  1838. receive_state_change(p_cli, NRF_CLI_RECEIVE_DEFAULT);
  1839. break;
  1840. }
  1841. }
  1842. }
  1843. /* Function remove white chars from beginning and end of command buffer. */
  1844. static void cmd_trim(nrf_cli_t const * p_cli)
  1845. {
  1846. nrf_cli_cmd_len_t i = 0;
  1847. if (p_cli->p_ctx->cmd_buff[0] == '\0') /* no command in the buffer */
  1848. {
  1849. return;
  1850. }
  1851. /* Counting white characters starting from beginning of the command. */
  1852. while (isspace((int)p_cli->p_ctx->cmd_buff[i++]))
  1853. {
  1854. if (i == 0)
  1855. {
  1856. p_cli->p_ctx->cmd_buff[0] = '\0';
  1857. return;
  1858. }
  1859. }
  1860. /* Removing counted white characters. */
  1861. if (--i > 0)
  1862. {
  1863. memmove(p_cli->p_ctx->cmd_buff,
  1864. p_cli->p_ctx->cmd_buff + i,
  1865. (p_cli->p_ctx->cmd_buff_len + 1) - i); /* +1 for '\0' */
  1866. p_cli->p_ctx->cmd_buff_len = p_cli->p_ctx->cmd_buff_len - i;
  1867. p_cli->p_ctx->cmd_buff_pos = p_cli->p_ctx->cmd_buff_len;
  1868. }
  1869. /* Counting white characters starting from end of command. */
  1870. char * p_end = &p_cli->p_ctx->cmd_buff[p_cli->p_ctx->cmd_buff_len - 1];
  1871. i = 0;
  1872. while (isspace((int)*p_end))
  1873. {
  1874. ++i;
  1875. --p_end;
  1876. }
  1877. /* Removing counted white characters. */
  1878. if (p_end != &p_cli->p_ctx->cmd_buff[p_cli->p_ctx->cmd_buff_len - 1])
  1879. {
  1880. p_cli->p_ctx->cmd_buff[p_cli->p_ctx->cmd_buff_len - i] = '\0';
  1881. p_cli->p_ctx->cmd_buff_len = p_cli->p_ctx->cmd_buff_len - i;
  1882. p_cli->p_ctx->cmd_buff_pos = p_cli->p_ctx->cmd_buff_len;
  1883. }
  1884. }
  1885. #if NRF_MODULE_ENABLED(NRF_CLI_WILDCARD)
  1886. static void spaces_trim(char * p_char)
  1887. {
  1888. nrf_cli_cmd_len_t shift = 0;
  1889. nrf_cli_cmd_len_t len = cli_strlen(p_char);
  1890. if (p_char == NULL)
  1891. {
  1892. return;
  1893. }
  1894. for (nrf_cli_cmd_len_t i = 0; i < len - 1; i++)
  1895. {
  1896. if (isspace((int)p_char[i]))
  1897. {
  1898. for (nrf_cli_cmd_len_t j = i + 1; j < len; j++)
  1899. {
  1900. if (isspace((int)p_char[j]))
  1901. {
  1902. shift++;
  1903. continue;
  1904. }
  1905. if (shift > 0)
  1906. {
  1907. memmove(&p_char[i + 1], &p_char[j], len - shift + 1); // +1 for EOS
  1908. len -= shift;
  1909. shift = 0;
  1910. }
  1911. break;
  1912. }
  1913. }
  1914. }
  1915. }
  1916. /* Adds new command and one space just before pattern */
  1917. static bool command_to_tmp_buffer_add(nrf_cli_t const * p_cli,
  1918. char const * p_new_cmd,
  1919. char const * p_pattern)
  1920. {
  1921. nrf_cli_cmd_len_t cmd_len = cli_strlen(p_new_cmd);
  1922. nrf_cli_cmd_len_t shift;
  1923. char * p_cmd_source_addr;
  1924. /* +1 for space */
  1925. if (((size_t)p_cli->p_ctx->cmd_tmp_buff_len + cmd_len + 1) > NRF_CLI_CMD_BUFF_SIZE)
  1926. {
  1927. nrf_cli_fprintf(p_cli,
  1928. NRF_CLI_WARNING,
  1929. "Command buffer is too short to expand all commands matching "
  1930. "wildcard pattern\r\n");
  1931. return false;
  1932. }
  1933. p_cmd_source_addr = strstr(p_cli->p_ctx->temp_buff, p_pattern);
  1934. if (p_cmd_source_addr == NULL)
  1935. {
  1936. return false;
  1937. }
  1938. shift = cli_strlen(p_cmd_source_addr);
  1939. /* make place for new command: + 1 for space + 1 for EOS */
  1940. memmove(p_cmd_source_addr + cmd_len + 1, p_cmd_source_addr, shift + 1);
  1941. memcpy(p_cmd_source_addr, p_new_cmd, cmd_len);
  1942. p_cmd_source_addr[cmd_len] = ' ';
  1943. p_cli->p_ctx->cmd_tmp_buff_len += cmd_len + 1; // + 1 for space
  1944. return true;
  1945. }
  1946. /* removes pattern and following space */
  1947. static void pattern_from_tmp_buffer_remove(nrf_cli_t const * p_cli,
  1948. char const * p_pattern)
  1949. {
  1950. size_t shift;
  1951. char * p_pattern_addr = strstr(p_cli->p_ctx->temp_buff, p_pattern);
  1952. nrf_cli_cmd_len_t pattern_len = cli_strlen(p_pattern);
  1953. if (p_pattern_addr == NULL)
  1954. {
  1955. return;
  1956. }
  1957. if (p_pattern_addr > p_cli->p_ctx->temp_buff)
  1958. {
  1959. if (*(p_pattern_addr - 1) == ' ')
  1960. {
  1961. pattern_len++; /* space needs to be removed as well */
  1962. p_pattern_addr--; /* set pointer to space */
  1963. }
  1964. }
  1965. shift = cli_strlen(p_pattern_addr) - pattern_len + 1; /* +1 for EOS */
  1966. p_cli->p_ctx->cmd_tmp_buff_len -= pattern_len;
  1967. memmove(p_pattern_addr, p_pattern_addr + pattern_len, shift);
  1968. }
  1969. /**
  1970. * @internal @brief Function for searching and adding commands matching to wildcard pattern.
  1971. *
  1972. * This function is internal to nrf_cli module and shall be not called directly.
  1973. *
  1974. * @param[in/out] p_cli Pointer to the CLI instance.
  1975. * @param[in] p_cmd Pointer to command which will be processed
  1976. * @param[in] cmd_lvl Command level in the command tree.
  1977. * @param[in] p_pattern Pointer to wildcard pattern.
  1978. * @param[out] p_counter Number of found and added commands.
  1979. *
  1980. * @retval WILDCARD_CMD_ADDED All matching commands added to the buffer.
  1981. * @retval WILDCARD_CMD_ADDED_MISSING_SPACE Not all matching commands added because
  1982. * NRF_CLI_CMD_BUFF_SIZE is too small.
  1983. * @retval WILDCARD_CMD_NO_MATCH_FOUND No matching command found.
  1984. */
  1985. static wildcard_cmd_status_t commands_expand(nrf_cli_t const * p_cli,
  1986. nrf_cli_cmd_entry_t const * p_cmd,
  1987. size_t cmd_lvl,
  1988. char * p_pattern,
  1989. size_t * p_counter)
  1990. {
  1991. size_t cmd_idx = 0;
  1992. size_t counter = 0;
  1993. bool success = false;
  1994. nrf_cli_static_entry_t static_entry;
  1995. nrf_cli_static_entry_t const * p_static_entry = NULL;
  1996. wildcard_cmd_status_t ret_val = WILDCARD_CMD_NO_MATCH_FOUND;
  1997. do
  1998. {
  1999. cmd_get(p_cmd,
  2000. cmd_lvl,
  2001. cmd_idx++,
  2002. &p_static_entry,
  2003. &static_entry);
  2004. if (p_static_entry == NULL)
  2005. {
  2006. break;
  2007. }
  2008. if (0 == fnmatch(p_pattern, p_static_entry->p_syntax, 0))
  2009. {
  2010. success = command_to_tmp_buffer_add(p_cli,
  2011. p_static_entry->p_syntax,
  2012. p_pattern);
  2013. if (!success)
  2014. {
  2015. break;
  2016. }
  2017. counter++;
  2018. }
  2019. } while(cmd_idx != 0);
  2020. if (counter > 0)
  2021. {
  2022. *p_counter = counter;
  2023. pattern_from_tmp_buffer_remove(p_cli, p_pattern);
  2024. if (success)
  2025. {
  2026. ret_val = WILDCARD_CMD_ADDED;
  2027. }
  2028. else
  2029. {
  2030. ret_val = WILDCARD_CMD_ADDED_MISSING_SPACE;
  2031. }
  2032. }
  2033. return ret_val;
  2034. }
  2035. #endif // NRF_MODULE_ENABLED(NRF_CLI_WILDCARD)
  2036. /* Function is analyzing the command buffer to find matching commands. Next, it invokes the last recognized
  2037. * command which has a handler and passes the rest of command buffer as arguments. */
  2038. static void cli_execute(nrf_cli_t const * p_cli)
  2039. {
  2040. char quote;
  2041. size_t argc;
  2042. char * argv[NRF_CLI_ARGC_MAX + 1]; /* +1 reserved for NULL added by function make_argv */
  2043. size_t cmd_idx; /* currently analyzed command in cmd_level */
  2044. size_t cmd_lvl = NRF_CLI_CMD_ROOT_LVL; /* currently analyzed command level */
  2045. size_t cmd_handler_lvl = 0; /* last command level for which a handler has been found */
  2046. size_t cmd_handler_idx = 0; /* last command index for which a handler has been found */
  2047. nrf_cli_cmd_entry_t const * p_cmd = NULL;
  2048. cmd_trim(p_cli);
  2049. #if NRF_MODULE_ENABLED(NRF_CLI_HISTORY)
  2050. history_save(p_cli);
  2051. #endif
  2052. #if NRF_MODULE_ENABLED(NRF_CLI_WILDCARD)
  2053. /* Wildcard can be correctly handled under following conditions:
  2054. - wildcard command does not have a handler
  2055. - wildcard command is on the deepest commands level
  2056. - other commands on the same level as wildcard command shall also not have a handler
  2057. Algorithm:
  2058. 1. Command buffer is copied to Temp buffer.
  2059. 2. Algorithm goes through Command buffer to find handlers and subcommands.
  2060. 3. If algorithm will find a wildcard character it switches to Temp buffer.
  2061. 4. In the Temp buffer command with found wildcard character is changed into matching command(s).
  2062. 5. Algorithm switch back to Command buffer and analyzes next command.
  2063. 6. When all arguments are analyzed from Command buffer, Temp buffer is copied to Command buffer.
  2064. 7. Last found handler is executed with all arguments in the Command buffer.
  2065. */
  2066. size_t commands_expanded = 0;
  2067. memset(p_cli->p_ctx->temp_buff, 0, sizeof(p_cli->p_ctx->temp_buff));
  2068. memcpy(p_cli->p_ctx->temp_buff,
  2069. p_cli->p_ctx->cmd_buff,
  2070. p_cli->p_ctx->cmd_buff_len);
  2071. /* Function spaces_trim must be used instead of make_argv. At this point it is important to keep
  2072. temp_buff as a one string. It will allow to find wildcard commands easly with strstr
  2073. function. */
  2074. spaces_trim(p_cli->p_ctx->temp_buff);
  2075. p_cli->p_ctx->cmd_tmp_buff_len = cli_strlen(p_cli->p_ctx->temp_buff) + 1; // +1 for EOS
  2076. #endif
  2077. cursor_end_position_move(p_cli);
  2078. if (!cursor_in_empty_line(p_cli))
  2079. {
  2080. cursor_next_line_move(p_cli);
  2081. }
  2082. /* create argument list */
  2083. quote = make_argv(&argc,
  2084. &argv[0],
  2085. p_cli->p_ctx->cmd_buff,
  2086. NRF_CLI_ARGC_MAX);
  2087. if (!argc)
  2088. {
  2089. cursor_next_line_move(p_cli);
  2090. return;
  2091. }
  2092. if (quote != 0)
  2093. {
  2094. nrf_cli_fprintf(p_cli, NRF_CLI_ERROR, "not terminated: %c\r\n", quote);
  2095. return;
  2096. }
  2097. /* Searching for a matching root command. */
  2098. for (cmd_idx = 0; cmd_idx <= CLI_DATA_SECTION_ITEM_COUNT; ++cmd_idx)
  2099. {
  2100. if (cmd_idx >= CLI_DATA_SECTION_ITEM_COUNT)
  2101. {
  2102. nrf_cli_fprintf(p_cli,
  2103. NRF_CLI_ERROR,
  2104. "%s%s\r\n",
  2105. argv[0],
  2106. NRF_CLI_MSG_COMMAND_NOT_FOUND);
  2107. return;
  2108. }
  2109. p_cmd = CLI_DATA_SECTION_ITEM_GET(cmd_idx);
  2110. if (strcmp(argv[cmd_lvl], p_cmd->u.p_static->p_syntax) != 0)
  2111. {
  2112. continue;
  2113. }
  2114. break;
  2115. }
  2116. /* Root command shall be always static. */
  2117. ASSERT(p_cmd->is_dynamic == false);
  2118. /* Pointer to the deepest command level with a handler. */
  2119. nrf_cli_cmd_entry_t const * p_cmd_low_level_entry = NULL;
  2120. /* Memory reserved for dynamic commands. */
  2121. nrf_cli_static_entry_t static_entry;
  2122. nrf_cli_static_entry_t const * p_static_entry = NULL;
  2123. nrf_cli_cmd_handler handler_cmd_lvl_0 = p_cmd->u.p_static->handler;
  2124. if (handler_cmd_lvl_0 != NULL)
  2125. {
  2126. p_cli->p_ctx->p_current_stcmd = p_cmd->u.p_static;
  2127. }
  2128. p_cmd = p_cmd->u.p_static->p_subcmd;
  2129. cmd_lvl++;
  2130. cmd_idx = 0;
  2131. while (1)
  2132. {
  2133. if (cmd_lvl >= argc)
  2134. {
  2135. break;
  2136. }
  2137. if (!strcmp(argv[cmd_lvl], "-h") || !strcmp(argv[cmd_lvl], "--help"))
  2138. {
  2139. /* Command called with help option so it makes no sense to search deeper commands. */
  2140. cli_flag_help_set(p_cli);
  2141. break;
  2142. }
  2143. #if NRF_MODULE_ENABLED(NRF_CLI_WILDCARD)
  2144. /* Wildcard character is found */
  2145. if (wildcard_character_exist(argv[cmd_lvl]))
  2146. {
  2147. size_t counter = 0;
  2148. wildcard_cmd_status_t status;
  2149. /* Function will search commands tree for commands matching wildcard pattern stored in
  2150. argv[cmd_lvl]. If match is found wildcard pattern will be replaced by matching
  2151. commands in temp_buffer. If there is no space to add all matching commands function
  2152. will add as many as possible. Next it will continue to search for next wildcard
  2153. pattern and it will try to add matching commands. */
  2154. status = commands_expand(p_cli, p_cmd, cmd_lvl, argv[cmd_lvl], &counter);
  2155. if (WILDCARD_CMD_NO_MATCH_FOUND == status)
  2156. {
  2157. break;
  2158. }
  2159. commands_expanded += counter;
  2160. cmd_lvl++;
  2161. continue;
  2162. }
  2163. #endif
  2164. cmd_get(p_cmd,
  2165. cmd_lvl,
  2166. cmd_idx++,
  2167. &p_static_entry,
  2168. &static_entry);
  2169. if ((cmd_idx == 0) || (p_static_entry == NULL))
  2170. {
  2171. break;
  2172. }
  2173. if (strcmp(argv[cmd_lvl], p_static_entry->p_syntax) == 0)
  2174. {
  2175. /* checking if command has a handler */
  2176. if (p_static_entry->handler != NULL)
  2177. {
  2178. #if NRF_MODULE_ENABLED(NRF_CLI_WILDCARD)
  2179. if (commands_expanded > 0)
  2180. {
  2181. cursor_end_position_move(p_cli);
  2182. if (!cursor_in_empty_line(p_cli))
  2183. {
  2184. cursor_next_line_move(p_cli);
  2185. }
  2186. /* An error occured, fnmatch argument cannot be followed by argument
  2187. * with a handler to avoid multiple function calls. */
  2188. nrf_cli_fprintf(p_cli,
  2189. NRF_CLI_ERROR,
  2190. "Error: requested multiple function executions\r\n");
  2191. cli_flag_help_clear(p_cli);
  2192. return;
  2193. }
  2194. #endif
  2195. /* Storing p_st_cmd->handler is not feasible for dynamic commands. Data will be
  2196. * invalid with the next loop iteration. */
  2197. cmd_handler_lvl = cmd_lvl;
  2198. cmd_handler_idx = cmd_idx - 1;
  2199. p_cmd_low_level_entry = p_cmd;
  2200. }
  2201. cmd_lvl++;
  2202. cmd_idx = 0;
  2203. p_cmd = p_static_entry->p_subcmd;
  2204. }
  2205. }
  2206. #if NRF_MODULE_ENABLED(NRF_CLI_WILDCARD)
  2207. if (commands_expanded > 0)
  2208. {
  2209. /* Copy temp_buff to cmd_buff */
  2210. memcpy(p_cli->p_ctx->cmd_buff,
  2211. p_cli->p_ctx->temp_buff,
  2212. p_cli->p_ctx->cmd_tmp_buff_len);
  2213. p_cli->p_ctx->cmd_buff_len = p_cli->p_ctx->cmd_tmp_buff_len;
  2214. /* calling make_arg function again because cmd_buffer has additional commads */
  2215. (void)make_argv(&argc,
  2216. &argv[0],
  2217. p_cli->p_ctx->cmd_buff,
  2218. NRF_CLI_ARGC_MAX);
  2219. }
  2220. #endif
  2221. /* Executing the deepest found handler. */
  2222. if (p_cmd_low_level_entry != NULL)
  2223. {
  2224. cmd_get(p_cmd_low_level_entry,
  2225. cmd_handler_lvl,
  2226. cmd_handler_idx,
  2227. &p_static_entry,
  2228. &static_entry);
  2229. p_cli->p_ctx->p_current_stcmd = p_static_entry;
  2230. p_cli->p_ctx->p_current_stcmd->handler(p_cli,
  2231. argc - cmd_handler_lvl,
  2232. &argv[cmd_handler_lvl]);
  2233. }
  2234. else if (handler_cmd_lvl_0 != NULL)
  2235. {
  2236. handler_cmd_lvl_0(p_cli, argc, &argv[0]);
  2237. }
  2238. else
  2239. {
  2240. nrf_cli_fprintf(p_cli, NRF_CLI_ERROR, NRF_CLI_MSG_SPECIFY_SUBCOMMAND);
  2241. }
  2242. cli_flag_help_clear(p_cli);
  2243. }
  2244. /* Function required by qsort. */
  2245. static int string_cmp(void const * pp_a, void const * pp_b)
  2246. {
  2247. ASSERT(pp_a);
  2248. ASSERT(pp_b);
  2249. char const ** pp_str_a = (char const **)pp_a;
  2250. char const ** pp_str_b = (char const **)pp_b;
  2251. return strcmp(*pp_str_a, *pp_str_b);
  2252. }
  2253. static void cli_transport_evt_handler(nrf_cli_transport_evt_t evt_type, void * p_context)
  2254. {
  2255. nrf_cli_t * p_cli = (nrf_cli_t *)p_context;
  2256. ASSERT(p_cli);
  2257. #if NRF_MODULE_ENABLED(NRF_CLI_USES_TASK_MANAGER)
  2258. task_events_set(p_cli->p_ctx->task_id, evt_type == NRF_CLI_TRANSPORT_EVT_RX_RDY ?
  2259. NRF_CLI_TRANSPORT_RX_RDY_TASK_EVT : NRF_CLI_TRANSPORT_TX_RDY_TASK_EVT);
  2260. #else
  2261. if (evt_type == NRF_CLI_TRANSPORT_EVT_RX_RDY)
  2262. {
  2263. }
  2264. else
  2265. {
  2266. /* wr done evt */
  2267. p_cli->p_ctx->internal.flag.tx_rdy = 1;
  2268. }
  2269. #endif
  2270. }
  2271. static ret_code_t nrf_cli_instance_init(nrf_cli_t const * p_cli,
  2272. void const * p_config,
  2273. bool use_colors)
  2274. {
  2275. ASSERT(p_cli);
  2276. ASSERT(p_cli->p_ctx && p_cli->p_iface && p_cli->p_name);
  2277. ASSERT((p_cli->newline_char == '\n') || (p_cli->newline_char == '\r'));
  2278. #if defined(NRF_CLI_LOG_BACKEND) && NRF_CLI_LOG_BACKEND
  2279. ((nrf_cli_log_backend_t *)p_cli->p_log_backend->p_ctx)->p_cli = p_cli;
  2280. #endif
  2281. ret_code_t ret = p_cli->p_iface->p_api->init(p_cli->p_iface,
  2282. p_config,
  2283. cli_transport_evt_handler,
  2284. (void *)p_cli);
  2285. if (ret != NRF_SUCCESS)
  2286. {
  2287. return ret;
  2288. }
  2289. #if NRF_MODULE_ENABLED(NRF_CLI_HISTORY)
  2290. ASSERT(p_cli->p_cmd_hist_mempool);
  2291. ret = nrf_memobj_pool_init(p_cli->p_cmd_hist_mempool);
  2292. if (ret != NRF_SUCCESS)
  2293. {
  2294. return ret;
  2295. }
  2296. p_cli->p_ctx->p_cmd_list_head = NULL;
  2297. p_cli->p_ctx->p_cmd_list_tail = NULL;
  2298. #endif
  2299. memset(p_cli->p_ctx, 0, sizeof(nrf_cli_ctx_t));
  2300. p_cli->p_ctx->internal.flag.tx_rdy = 1;
  2301. #if NRF_MODULE_ENABLED(NRF_CLI_VT100_COLORS)
  2302. p_cli->p_ctx->internal.flag.use_colors = use_colors;
  2303. #endif
  2304. p_cli->p_ctx->internal.flag.echo = NRF_CLI_ECHO_STATUS;
  2305. p_cli->p_ctx->state = NRF_CLI_STATE_INITIALIZED;
  2306. p_cli->p_ctx->vt100_ctx.cons.terminal_wid = NRF_CLI_DEFAULT_TERMINAL_WIDTH;
  2307. p_cli->p_ctx->vt100_ctx.cons.terminal_hei = NRF_CLI_DEFAULT_TERMINAL_HEIGHT;
  2308. const char * * pp_sorted_cmds = (const char * *)CLI_SORTED_CMD_PTRS_START_ADDR_GET;
  2309. for (size_t i = 0; i < CLI_DATA_SECTION_ITEM_COUNT; i++)
  2310. {
  2311. const nrf_cli_cmd_entry_t * cmd;
  2312. cmd = CLI_DATA_SECTION_ITEM_GET(i);
  2313. /* NULL syntax commands not allowed. */
  2314. ASSERT(cmd);
  2315. ASSERT(cmd->u.p_static->p_syntax);
  2316. pp_sorted_cmds[i] = cmd->u.p_static->p_syntax;
  2317. }
  2318. if (CLI_DATA_SECTION_ITEM_COUNT > 0)
  2319. {
  2320. qsort(pp_sorted_cmds,
  2321. CLI_DATA_SECTION_ITEM_COUNT,
  2322. sizeof (char *),
  2323. string_cmp);
  2324. }
  2325. return NRF_SUCCESS;
  2326. }
  2327. #if NRF_MODULE_ENABLED(NRF_CLI_USES_TASK_MANAGER)
  2328. static ret_code_t nrf_cli_instance_uninit(nrf_cli_t const * p_cli);
  2329. void console_task(void * p_context)
  2330. {
  2331. nrf_cli_t * p_cli = (nrf_cli_t *)p_context;
  2332. ret_code_t ret = nrf_cli_start(p_cli);
  2333. APP_ERROR_CHECK(ret);
  2334. while (1)
  2335. {
  2336. uint32_t evts = task_events_wait(NRF_CLI_TASK_EVTS);
  2337. if (evts & NRF_CLI_KILL_TASK_EVT)
  2338. {
  2339. (void)nrf_cli_instance_uninit(p_cli);
  2340. task_exit();
  2341. }
  2342. else
  2343. {
  2344. nrf_cli_process(p_cli);
  2345. }
  2346. }
  2347. }
  2348. #endif
  2349. ret_code_t nrf_cli_init(nrf_cli_t const * p_cli,
  2350. void const * p_config,
  2351. bool use_colors,
  2352. bool log_backend,
  2353. nrf_log_severity_t init_lvl)
  2354. {
  2355. ASSERT(p_cli);
  2356. ret_code_t err_code = nrf_cli_instance_init(p_cli, p_config, use_colors);
  2357. #if NRF_CLI_LOG_BACKEND && NRF_MODULE_ENABLED(NRF_LOG)
  2358. if ((err_code == NRF_SUCCESS) && log_backend && NRF_CLI_LOG_BACKEND)
  2359. {
  2360. int32_t id = nrf_log_backend_add(p_cli->p_log_backend, init_lvl);
  2361. if (id < 0)
  2362. {
  2363. return NRF_ERROR_NO_MEM;
  2364. }
  2365. nrf_log_backend_enable(p_cli->p_log_backend);
  2366. }
  2367. #endif
  2368. return err_code;
  2369. }
  2370. ret_code_t nrf_cli_task_create(nrf_cli_t const * p_cli)
  2371. {
  2372. #if NRF_MODULE_ENABLED(NRF_CLI_USES_TASK_MANAGER)
  2373. p_cli->p_ctx->task_id = task_create(console_task, p_cli->p_name,(void *)p_cli);
  2374. if (p_cli->p_ctx->task_id == TASK_ID_INVALID)
  2375. {
  2376. return NRF_ERROR_NO_MEM;
  2377. }
  2378. else
  2379. {
  2380. return NRF_SUCCESS;
  2381. }
  2382. #else
  2383. return NRF_ERROR_NOT_SUPPORTED;
  2384. #endif
  2385. }
  2386. static ret_code_t nrf_cli_instance_uninit(nrf_cli_t const * p_cli)
  2387. {
  2388. ASSERT(p_cli);
  2389. ASSERT(p_cli->p_ctx && p_cli->p_iface && p_cli->p_name);
  2390. if (cli_flag_processing_is_set(p_cli))
  2391. {
  2392. return NRF_ERROR_BUSY;
  2393. }
  2394. #if NRF_CLI_LOG_BACKEND && NRF_MODULE_ENABLED(NRF_LOG)
  2395. if (p_cli->p_log_backend != NULL)
  2396. {
  2397. nrf_log_backend_disable(p_cli->p_log_backend);
  2398. nrf_log_backend_remove(p_cli->p_log_backend);
  2399. }
  2400. #endif
  2401. ret_code_t ret = p_cli->p_iface->p_api->uninit(p_cli->p_iface);
  2402. if (ret != NRF_SUCCESS)
  2403. {
  2404. return ret;
  2405. }
  2406. #if NRF_MODULE_ENABLED(NRF_CLI_HISTORY)
  2407. history_list_free_memory(p_cli);
  2408. #endif
  2409. memset(p_cli->p_ctx, 0, sizeof(nrf_cli_ctx_t));
  2410. p_cli->p_ctx->state = NRF_CLI_STATE_UNINITIALIZED;
  2411. return NRF_SUCCESS;
  2412. }
  2413. ret_code_t nrf_cli_uninit(nrf_cli_t const * p_cli)
  2414. {
  2415. #if NRF_MODULE_ENABLED(NRF_CLI_USES_TASK_MANAGER)
  2416. if (cli_flag_processing_is_set(p_cli))
  2417. {
  2418. return NRF_ERROR_BUSY;
  2419. }
  2420. task_events_set(p_cli->p_ctx->task_id, NRF_CLI_KILL_TASK_EVT);
  2421. return NRF_SUCCESS;
  2422. #else
  2423. return nrf_cli_instance_uninit(p_cli);
  2424. #endif
  2425. }
  2426. ret_code_t nrf_cli_start(nrf_cli_t const * p_cli)
  2427. {
  2428. ASSERT(p_cli);
  2429. ASSERT(p_cli->p_ctx && p_cli->p_iface && p_cli->p_name);
  2430. if (p_cli->p_ctx->state != NRF_CLI_STATE_INITIALIZED)
  2431. {
  2432. return NRF_ERROR_INVALID_STATE;
  2433. }
  2434. #if NRF_MODULE_ENABLED(NRF_CLI_USES_TASK_MANAGER)
  2435. void * p_context = (void *)((uint32_t)task_id_get());
  2436. ((nrf_cli_log_backend_t *)p_cli->p_log_backend->p_ctx)->p_context = p_context;
  2437. #endif
  2438. ret_code_t err_code = p_cli->p_iface->p_api->enable(p_cli->p_iface, false);
  2439. #if NRF_MODULE_ENABLED(NRF_CLI_USES_TASK_MANAGER)
  2440. task_events_set(task_id_get(), NRF_CLI_TRANSPORT_RX_RDY_TASK_EVT);
  2441. #endif
  2442. if (err_code == NRF_SUCCESS)
  2443. {
  2444. #if NRF_CLI_VT100_COLORS_ENABLED
  2445. vt100_color_set(p_cli, NRF_CLI_NORMAL);
  2446. vt100_bgcolor_set(p_cli, NRF_CLI_VT100_COLOR_BLACK);
  2447. #endif
  2448. nrf_fprintf(p_cli->p_fprintf_ctx, "\r\n\n");
  2449. cli_state_set(p_cli, NRF_CLI_STATE_ACTIVE);
  2450. }
  2451. return err_code;
  2452. }
  2453. ret_code_t nrf_cli_stop(nrf_cli_t const * p_cli)
  2454. {
  2455. ASSERT(p_cli);
  2456. ASSERT(p_cli->p_ctx && p_cli->p_iface && p_cli->p_name);
  2457. if (p_cli->p_ctx->state == NRF_CLI_STATE_INITIALIZED ||
  2458. p_cli->p_ctx->state == NRF_CLI_STATE_UNINITIALIZED)
  2459. {
  2460. return NRF_ERROR_INVALID_STATE;
  2461. }
  2462. cli_state_set(p_cli, NRF_CLI_STATE_INITIALIZED);
  2463. return NRF_SUCCESS;
  2464. }
  2465. void nrf_cli_process(nrf_cli_t const * p_cli)
  2466. {
  2467. ASSERT(p_cli);
  2468. ASSERT(p_cli->p_ctx && p_cli->p_iface && p_cli->p_name);
  2469. nrf_cli_internal_t internal;
  2470. internal.value = 0;
  2471. internal.flag.processing = 1;
  2472. (void)nrf_atomic_u32_or((nrf_atomic_u32_t *)&p_cli->p_ctx->internal.value,
  2473. internal.value);
  2474. switch (p_cli->p_ctx->state)
  2475. {
  2476. case NRF_CLI_STATE_UNINITIALIZED:
  2477. case NRF_CLI_STATE_INITIALIZED:
  2478. /* Console initialized but not started. */
  2479. break;
  2480. case NRF_CLI_STATE_ACTIVE:
  2481. {
  2482. cli_state_collect(p_cli);
  2483. bool log_processed = cli_log_entry_process(p_cli, false);
  2484. if (log_processed)
  2485. {
  2486. nrf_cli_fprintf(p_cli, NRF_CLI_INFO, "%s", p_cli->p_name);
  2487. if (cli_flag_echo_is_set(p_cli))
  2488. {
  2489. nrf_cli_fprintf(p_cli, NRF_CLI_NORMAL, "%s", p_cli->p_ctx->cmd_buff);
  2490. cursor_position_synchronize(p_cli);
  2491. }
  2492. }
  2493. break;
  2494. }
  2495. default:
  2496. break;
  2497. }
  2498. transport_buffer_flush(p_cli);
  2499. internal.value = (uint32_t)0xFFFFFFFF;
  2500. internal.flag.processing = 0;
  2501. (void)nrf_atomic_u32_and((nrf_atomic_u32_t *)&p_cli->p_ctx->internal.value,
  2502. internal.value);
  2503. }
  2504. /* Function shall be only used by the nrf_fprintf module. */
  2505. void nrf_cli_print_stream(void const * p_user_ctx, char const * p_data, size_t data_len)
  2506. {
  2507. cli_write((nrf_cli_t const *)p_user_ctx,
  2508. p_data,
  2509. data_len,
  2510. NULL);
  2511. }
  2512. void nrf_cli_fprintf(nrf_cli_t const * p_cli,
  2513. nrf_cli_vt100_color_t color,
  2514. char const * p_fmt,
  2515. ...)
  2516. {
  2517. ASSERT(p_fmt);
  2518. ASSERT(p_cli);
  2519. ASSERT(p_cli->p_ctx && p_cli->p_iface && p_cli->p_name);
  2520. va_list args = {0};
  2521. va_start(args, p_fmt);
  2522. #if NRF_MODULE_ENABLED(NRF_CLI_VT100_COLORS)
  2523. if ((p_cli->p_ctx->internal.flag.use_colors) &&
  2524. (color != p_cli->p_ctx->vt100_ctx.col.col))
  2525. {
  2526. nrf_cli_vt100_colors_t col;
  2527. vt100_colors_store(p_cli, &col);
  2528. vt100_color_set(p_cli, color);
  2529. nrf_fprintf_fmt(p_cli->p_fprintf_ctx, p_fmt, &args);
  2530. vt100_colors_restore(p_cli, &col);
  2531. }
  2532. else
  2533. #endif // NRF_MODULE_ENABLED(NRF_CLI_VT100_COLORS)
  2534. {
  2535. nrf_fprintf_fmt(p_cli->p_fprintf_ctx, p_fmt, &args);
  2536. }
  2537. va_end(args);
  2538. }
  2539. /* Function prints a string on terminal screen with requested margin.
  2540. * It takes care to not divide words.
  2541. * p_cli Pointer to CLI instance.
  2542. * p_str Pointer to string to be printed.
  2543. * terminal_offset Requested left margin.
  2544. * offset_first_line Add margin to the first printed line.
  2545. */
  2546. static void format_offset_string_print(nrf_cli_t const * p_cli,
  2547. char const * p_str,
  2548. size_t terminal_offset,
  2549. bool offset_first_line)
  2550. {
  2551. if (p_str == NULL)
  2552. {
  2553. return;
  2554. }
  2555. if (offset_first_line)
  2556. {
  2557. cursor_right_move(p_cli, terminal_offset);
  2558. }
  2559. size_t length;
  2560. size_t offset = 0;
  2561. /* Skipping whitespace. */
  2562. while (isspace((int)*(p_str + offset)))
  2563. {
  2564. ++offset;
  2565. }
  2566. while (1)
  2567. {
  2568. size_t idx = 0;
  2569. length = cli_strlen(p_str) - offset;
  2570. if (length <= p_cli->p_ctx->vt100_ctx.cons.terminal_wid - terminal_offset)
  2571. {
  2572. for (idx = 0; idx < length; idx++)
  2573. {
  2574. if (*(p_str + offset + idx) == '\n')
  2575. {
  2576. transport_buffer_flush(p_cli);
  2577. cli_write(p_cli, p_str + offset, idx, NULL);
  2578. offset += idx + 1;
  2579. cursor_next_line_move(p_cli);
  2580. cursor_right_move(p_cli, terminal_offset);
  2581. break;
  2582. }
  2583. }
  2584. /* String will fit in one line. */
  2585. nrf_fprintf(p_cli->p_fprintf_ctx, p_str + offset);
  2586. break;
  2587. }
  2588. else
  2589. {
  2590. /* String is longer than terminal line so text needs to divide in the way
  2591. to not divide words. */
  2592. length = p_cli->p_ctx->vt100_ctx.cons.terminal_wid - terminal_offset;
  2593. while (1)
  2594. {
  2595. /* Determining line break. */
  2596. if (isspace((int)(*(p_str + offset + idx))))
  2597. {
  2598. length = idx;
  2599. if (*(p_str + offset + idx) == '\n')
  2600. {
  2601. break;
  2602. }
  2603. }
  2604. if ((idx + terminal_offset) >= p_cli->p_ctx->vt100_ctx.cons.terminal_wid)
  2605. {
  2606. /* End of line reached. */
  2607. break;
  2608. }
  2609. ++idx;
  2610. }
  2611. /* Writing one line, fprintf IO buffer must be flushed before calling cli_write. */
  2612. transport_buffer_flush(p_cli);
  2613. cli_write(p_cli, p_str + offset, length, NULL);
  2614. offset += length;
  2615. /* Calculating text offset to ensure that next line will not begin with a space. */
  2616. while (isspace((int)(*(p_str + offset))))
  2617. {
  2618. ++offset;
  2619. }
  2620. cursor_next_line_move(p_cli);
  2621. cursor_right_move(p_cli, terminal_offset);
  2622. }
  2623. }
  2624. cursor_next_line_move(p_cli);
  2625. }
  2626. void nrf_cli_help_print(nrf_cli_t const * p_cli,
  2627. nrf_cli_getopt_option_t const * p_opt,
  2628. size_t opt_len)
  2629. {
  2630. ASSERT(p_cli);
  2631. ASSERT(p_cli->p_ctx && p_cli->p_iface && p_cli->p_name);
  2632. static uint8_t const tab_len = 2;
  2633. static char const opt_sep[] =", "; /* options separator */
  2634. static char const help[] = "-h, --help";
  2635. static char const cmd_sep[] = " - "; /* command separator */
  2636. uint16_t field_width = 0;
  2637. uint16_t longest_string = cli_strlen(help) - cli_strlen(opt_sep);
  2638. /* Printing help string for command. */
  2639. nrf_cli_fprintf(p_cli,
  2640. NRF_CLI_NORMAL,
  2641. "%s%s",
  2642. p_cli->p_ctx->p_current_stcmd->p_syntax,
  2643. cmd_sep);
  2644. field_width = cli_strlen(p_cli->p_ctx->p_current_stcmd->p_syntax) + cli_strlen(cmd_sep);
  2645. format_offset_string_print(p_cli, p_cli->p_ctx->p_current_stcmd->p_help, field_width, false);
  2646. nrf_cli_fprintf(p_cli, NRF_CLI_NORMAL, "Options:\r\n");
  2647. /* Looking for the longest option string. */
  2648. if ((opt_len > 0) && (p_opt != NULL))
  2649. {
  2650. for (size_t i = 0; i < opt_len; ++i)
  2651. {
  2652. if (cli_strlen(p_opt[i].p_optname_short) + cli_strlen(p_opt[i].p_optname)
  2653. > longest_string)
  2654. {
  2655. longest_string = cli_strlen(p_opt[i].p_optname_short)
  2656. + cli_strlen(p_opt[i].p_optname);
  2657. }
  2658. }
  2659. }
  2660. longest_string += cli_strlen(opt_sep) + tab_len;
  2661. nrf_cli_fprintf(p_cli,
  2662. NRF_CLI_NORMAL,
  2663. " %-*s:",
  2664. longest_string,
  2665. help);
  2666. /* Print help string for options (only -h and --help). */
  2667. field_width = longest_string + tab_len + 1; /* tab_len + 1 == " " and ':' from: " %-*s:" */
  2668. format_offset_string_print(p_cli, "Show command help.", field_width, false);
  2669. /* Formating and printing all available options (except -h, --help). */
  2670. if (p_opt != NULL)
  2671. {
  2672. for (size_t i = 0; i < opt_len; ++i)
  2673. {
  2674. if ((p_opt[i].p_optname_short != NULL) && (p_opt[i].p_optname != NULL))
  2675. {
  2676. nrf_cli_fprintf(p_cli,
  2677. NRF_CLI_NORMAL,
  2678. " %s%s%s",
  2679. p_opt[i].p_optname_short,
  2680. opt_sep,
  2681. p_opt[i].p_optname);
  2682. field_width = longest_string + tab_len;
  2683. cursor_right_move(p_cli,
  2684. field_width - ( cli_strlen(p_opt[i].p_optname_short)
  2685. + cli_strlen(p_opt[i].p_optname)
  2686. + tab_len
  2687. + cli_strlen(opt_sep)));
  2688. cli_putc(p_cli, ':');
  2689. ++field_width; /* incrementing because char ':' was already printed above */
  2690. }
  2691. else if (p_opt[i].p_optname_short != NULL)
  2692. {
  2693. nrf_cli_fprintf(p_cli,
  2694. NRF_CLI_NORMAL,
  2695. " %-*s:",
  2696. longest_string,
  2697. p_opt[i].p_optname_short);
  2698. /* tab_len + 1 == " " and ':' from: " %-*s:" */
  2699. field_width = longest_string + tab_len + 1;
  2700. }
  2701. else if (p_opt[i].p_optname != NULL)
  2702. {
  2703. nrf_cli_fprintf(p_cli,
  2704. NRF_CLI_NORMAL,
  2705. " %-*s:",
  2706. longest_string,
  2707. p_opt[i].p_optname);
  2708. /* tab_len + 1 == " " and ':' from: " %-*s:" */
  2709. field_width = longest_string + tab_len + 1;
  2710. }
  2711. else
  2712. {
  2713. /* Do nothing. */
  2714. }
  2715. if (p_opt[i].p_optname_help != NULL)
  2716. {
  2717. format_offset_string_print(p_cli, p_opt[i].p_optname_help, field_width, false);
  2718. }
  2719. else
  2720. {
  2721. cursor_next_line_move(p_cli);
  2722. }
  2723. }
  2724. }
  2725. /* Checking if there are any subcommands avilable. */
  2726. if (p_cli->p_ctx->p_current_stcmd->p_subcmd == NULL)
  2727. {
  2728. return;
  2729. }
  2730. /* Printing formatted help of one level deeper subcommands. */
  2731. nrf_cli_static_entry_t static_entry;
  2732. nrf_cli_cmd_entry_t const * p_cmd = p_cli->p_ctx->p_current_stcmd->p_subcmd;
  2733. nrf_cli_static_entry_t const * p_st_cmd = NULL;
  2734. field_width = 0;
  2735. longest_string = 0;
  2736. size_t cmd_idx = 0;
  2737. /* Searching for the longest subcommand to print. */
  2738. while (1)
  2739. {
  2740. cmd_get(p_cmd, !NRF_CLI_CMD_ROOT_LVL, cmd_idx++, &p_st_cmd, &static_entry);
  2741. if (p_st_cmd == NULL)
  2742. {
  2743. break;
  2744. }
  2745. if (cli_strlen(p_st_cmd->p_syntax) > longest_string)
  2746. {
  2747. longest_string = cli_strlen(p_st_cmd->p_syntax);
  2748. }
  2749. }
  2750. /* Checking if there are dynamic subcommands. */
  2751. if (cmd_idx == 1)
  2752. {
  2753. /* No dynamic subcommands available. */
  2754. return;
  2755. }
  2756. nrf_cli_fprintf(p_cli, NRF_CLI_NORMAL, "Subcommands:\r\n");
  2757. /* Printing subcommands and help string (if exists). */
  2758. cmd_idx = 0;
  2759. while (1)
  2760. {
  2761. cmd_get(p_cmd, !NRF_CLI_CMD_ROOT_LVL, cmd_idx++, &p_st_cmd, &static_entry);
  2762. if (p_st_cmd == NULL)
  2763. {
  2764. break;
  2765. }
  2766. field_width = longest_string + tab_len;
  2767. nrf_cli_fprintf(p_cli, NRF_CLI_NORMAL," %-*s:", field_width, p_st_cmd->p_syntax);
  2768. field_width += tab_len + 1; /* tab_len + 1 == " " and ':' from: " %-*s:" */
  2769. if (p_st_cmd->p_help != NULL)
  2770. {
  2771. format_offset_string_print(p_cli, p_st_cmd->p_help, field_width, false);
  2772. }
  2773. else
  2774. {
  2775. cursor_next_line_move(p_cli);
  2776. }
  2777. }
  2778. }
  2779. #if NRF_CLI_LOG_BACKEND && NRF_MODULE_ENABLED(NRF_LOG)
  2780. #define NRF_CLI_LOG_MSG_OVERFLOW_MSK ((uint32_t)7)
  2781. static bool cli_log_entry_process(nrf_cli_t const * p_cli, bool skip)
  2782. {
  2783. nrf_log_entry_t entry;
  2784. #if NRF_MODULE_ENABLED(NRF_CLI_STATISTICS)
  2785. bool print_msg = false;
  2786. #endif
  2787. if (nrf_queue_pop(((nrf_cli_log_backend_t *)p_cli->p_log_backend->p_ctx)->p_queue, &entry) !=
  2788. NRF_SUCCESS)
  2789. {
  2790. return false;
  2791. }
  2792. if (skip)
  2793. {
  2794. nrf_memobj_put(entry);
  2795. #if NRF_MODULE_ENABLED(NRF_CLI_STATISTICS)
  2796. ++p_cli->p_ctx->statistics.log_lost_cnt;
  2797. if ((p_cli->p_ctx->statistics.log_lost_cnt & NRF_CLI_LOG_MSG_OVERFLOW_MSK) == 1)
  2798. {
  2799. /* Set flag to print a message after clearing the currently entered command. */
  2800. print_msg = true;
  2801. }
  2802. else
  2803. #endif
  2804. {
  2805. return true;
  2806. }
  2807. }
  2808. {
  2809. /* Erasing the currently displayed command and console name. */
  2810. nrf_cli_multiline_cons_t const * p_cons = multiline_console_data_check(p_cli);
  2811. if (p_cons->cur_y > NRF_CLI_INITIAL_CURS_POS)
  2812. {
  2813. cursor_up_move(p_cli, p_cons->cur_y - NRF_CLI_INITIAL_CURS_POS);
  2814. }
  2815. if (p_cons->cur_x > NRF_CLI_INITIAL_CURS_POS)
  2816. {
  2817. cursor_left_move(p_cli, p_cons->cur_x - NRF_CLI_INITIAL_CURS_POS);
  2818. }
  2819. cli_clear_eos(p_cli);
  2820. }
  2821. #if NRF_MODULE_ENABLED(NRF_CLI_STATISTICS)
  2822. if (print_msg)
  2823. {
  2824. /* Print the requested string and exit function. */
  2825. nrf_cli_fprintf(p_cli, NRF_CLI_ERROR, "Lost logs - increase log backend queue size.\r\n");
  2826. return true;
  2827. }
  2828. #endif
  2829. /* Printing logs from the queue. */
  2830. do
  2831. {
  2832. nrf_log_header_t header;
  2833. size_t memobj_offset = 0;
  2834. nrf_log_str_formatter_entry_params_t params;
  2835. nrf_memobj_read(entry, &header, HEADER_SIZE*sizeof(uint32_t), memobj_offset);
  2836. memobj_offset = HEADER_SIZE * sizeof(uint32_t);
  2837. params.timestamp = header.timestamp;
  2838. params.module_id = header.module_id;
  2839. params.dropped = header.dropped;
  2840. params.use_colors = NRF_LOG_USES_COLORS; /* Color will be provided by the console application. */
  2841. if (header.base.generic.type == HEADER_TYPE_STD)
  2842. {
  2843. char const * p_log_str = (char const *)((uint32_t)header.base.std.addr);
  2844. params.severity = (nrf_log_severity_t)header.base.std.severity;
  2845. uint32_t nargs = header.base.std.nargs;
  2846. uint32_t args[6];
  2847. nrf_memobj_read(entry, args, nargs*sizeof(uint32_t), memobj_offset);
  2848. nrf_log_std_entry_process(p_log_str,
  2849. args,
  2850. nargs,
  2851. &params,
  2852. p_cli->p_fprintf_ctx);
  2853. }
  2854. else if (header.base.generic.type == HEADER_TYPE_HEXDUMP)
  2855. {
  2856. uint32_t data_len;
  2857. uint8_t data_buf[8];
  2858. uint32_t chunk_len;
  2859. data_len = header.base.hexdump.len;
  2860. params.severity = (nrf_log_severity_t)header.base.hexdump.severity;
  2861. do
  2862. {
  2863. chunk_len = sizeof(data_buf) > data_len ? data_len : sizeof(data_buf);
  2864. nrf_memobj_read(entry, data_buf, chunk_len, memobj_offset);
  2865. memobj_offset += chunk_len;
  2866. data_len -= chunk_len;
  2867. nrf_log_hexdump_entry_process(data_buf, chunk_len, &params, p_cli->p_fprintf_ctx);
  2868. } while (data_len > 0);
  2869. }
  2870. nrf_memobj_put(entry);
  2871. } while (nrf_queue_pop(((nrf_cli_log_backend_t *)p_cli->p_log_backend->p_ctx)->p_queue, &entry)
  2872. == NRF_SUCCESS);
  2873. return true;
  2874. }
  2875. static void nrf_log_backend_cli_put(nrf_log_backend_t const * p_backend, nrf_log_entry_t * p_msg)
  2876. {
  2877. nrf_cli_log_backend_t * p_backend_cli = (nrf_cli_log_backend_t *)p_backend->p_ctx;
  2878. nrf_cli_t const * p_cli = p_backend_cli->p_cli;
  2879. //If panic mode cannot be handled, stop handling new requests.
  2880. if (p_cli->p_ctx->state != NRF_CLI_STATE_PANIC_MODE_INACTIVE)
  2881. {
  2882. bool panic_mode = (p_cli->p_ctx->state == NRF_CLI_STATE_PANIC_MODE_ACTIVE);
  2883. //If there is no place for a new log entry, remove the oldest one.
  2884. ret_code_t err_code = nrf_queue_push(p_backend_cli->p_queue, &p_msg);
  2885. while (err_code != NRF_SUCCESS)
  2886. {
  2887. (void)cli_log_entry_process(p_cli, panic_mode ? false : true);
  2888. err_code = nrf_queue_push(p_backend_cli->p_queue, &p_msg);
  2889. }
  2890. nrf_memobj_get(p_msg);
  2891. if (panic_mode)
  2892. {
  2893. (void)cli_log_entry_process(p_cli, false);
  2894. }
  2895. #if NRF_MODULE_ENABLED(NRF_CLI_USES_TASK_MANAGER)
  2896. else
  2897. {
  2898. task_events_set((task_id_t)((uint32_t)p_backend_cli->p_context & 0x000000FF),
  2899. NRF_CLI_LOG_PENDING_TASK_EVT);
  2900. }
  2901. #endif
  2902. }
  2903. }
  2904. static void nrf_log_backend_cli_flush(nrf_log_backend_t const * p_backend)
  2905. {
  2906. nrf_cli_log_backend_t * p_backend_cli = (nrf_cli_log_backend_t *)p_backend->p_ctx;
  2907. nrf_cli_t const * p_cli = p_backend_cli->p_cli;
  2908. nrf_log_entry_t * p_msg;
  2909. if (nrf_queue_pop(p_backend_cli->p_queue, &p_msg) == NRF_SUCCESS)
  2910. {
  2911. (void)cli_log_entry_process(p_cli, false);
  2912. }
  2913. UNUSED_PARAMETER(p_backend);
  2914. }
  2915. static void nrf_log_backend_cli_panic_set(nrf_log_backend_t const * p_backend)
  2916. {
  2917. nrf_cli_log_backend_t * p_backend_cli = (nrf_cli_log_backend_t *)p_backend->p_ctx;
  2918. nrf_cli_t const * p_cli = p_backend_cli->p_cli;
  2919. if (p_cli->p_iface->p_api->enable(p_cli->p_iface, true) == NRF_SUCCESS)
  2920. {
  2921. p_cli->p_ctx->state = NRF_CLI_STATE_PANIC_MODE_ACTIVE;
  2922. }
  2923. else
  2924. {
  2925. p_cli->p_ctx->state = NRF_CLI_STATE_PANIC_MODE_INACTIVE;
  2926. }
  2927. }
  2928. const nrf_log_backend_api_t nrf_log_backend_cli_api = {
  2929. .put = nrf_log_backend_cli_put,
  2930. .flush = nrf_log_backend_cli_flush,
  2931. .panic_set = nrf_log_backend_cli_panic_set,
  2932. };
  2933. #else
  2934. static bool cli_log_entry_process(nrf_cli_t const * p_cli, bool skip)
  2935. {
  2936. UNUSED_PARAMETER(p_cli);
  2937. UNUSED_PARAMETER(skip);
  2938. return false;
  2939. }
  2940. #endif // NRF_CLI_LOG_BACKEND
  2941. /* ============ built-in commands ============ */
  2942. #if NRF_MODULE_ENABLED(NRF_CLI_BUILD_IN_CMDS)
  2943. static bool nrf_cli_build_in_cmd_common_executed(nrf_cli_t const * p_cli,
  2944. bool arg_cnt_nok,
  2945. nrf_cli_getopt_option_t const * p_opt,
  2946. size_t opt_len)
  2947. {
  2948. if (nrf_cli_help_requested(p_cli))
  2949. {
  2950. nrf_cli_help_print(p_cli, p_opt, opt_len);
  2951. return true;
  2952. }
  2953. if (arg_cnt_nok)
  2954. {
  2955. nrf_cli_fprintf(p_cli,
  2956. NRF_CLI_ERROR,
  2957. "%s: wrong parameter count\r\n",
  2958. p_cli->p_ctx->p_current_stcmd->p_syntax);
  2959. return true;
  2960. }
  2961. return false;
  2962. }
  2963. static void nrf_cli_cmd_clear(nrf_cli_t const * p_cli, size_t argc, char **argv)
  2964. {
  2965. ASSERT(p_cli);
  2966. ASSERT(p_cli->p_ctx && p_cli->p_iface && p_cli->p_name);
  2967. UNUSED_PARAMETER(argv);
  2968. if ((argc == 2) && (nrf_cli_help_requested(p_cli)))
  2969. {
  2970. nrf_cli_help_print(p_cli, NULL, 0);
  2971. return;
  2972. }
  2973. NRF_CLI_VT100_CMD(p_cli, NRF_CLI_VT100_CURSORHOME);
  2974. NRF_CLI_VT100_CMD(p_cli, NRF_CLI_VT100_CLEARSCREEN);
  2975. }
  2976. static void nrf_cli_cmd_cli(nrf_cli_t const * p_cli, size_t argc, char **argv)
  2977. {
  2978. ASSERT(p_cli);
  2979. ASSERT(p_cli->p_ctx && p_cli->p_iface && p_cli->p_name);
  2980. UNUSED_PARAMETER(argv);
  2981. if ((argc == 1) || ((argc == 2) && nrf_cli_help_requested(p_cli)) )
  2982. {
  2983. nrf_cli_help_print(p_cli, NULL, 0);
  2984. return;
  2985. }
  2986. nrf_cli_fprintf(p_cli, NRF_CLI_ERROR, NRF_CLI_MSG_SPECIFY_SUBCOMMAND);
  2987. }
  2988. #if NRF_MODULE_ENABLED(NRF_CLI_VT100_COLORS)
  2989. static void nrf_cli_cmd_colors_off(nrf_cli_t const * p_cli, size_t argc, char **argv)
  2990. {
  2991. if (nrf_cli_build_in_cmd_common_executed(p_cli, (argc != 1), NULL, 0))
  2992. {
  2993. return;
  2994. }
  2995. p_cli->p_ctx->internal.flag.use_colors = 0;
  2996. }
  2997. static void nrf_cli_cmd_colors_on(nrf_cli_t const * p_cli, size_t argc, char **argv)
  2998. {
  2999. if (nrf_cli_build_in_cmd_common_executed(p_cli, (argc != 1), NULL, 0))
  3000. {
  3001. return;
  3002. }
  3003. p_cli->p_ctx->internal.flag.use_colors = 1;
  3004. }
  3005. static void nrf_cli_cmd_colors(nrf_cli_t const * p_cli, size_t argc, char **argv)
  3006. {
  3007. ASSERT(p_cli);
  3008. ASSERT(p_cli->p_ctx && p_cli->p_iface && p_cli->p_name);
  3009. if (argc == 1)
  3010. {
  3011. nrf_cli_help_print(p_cli, NULL, 0);
  3012. return;
  3013. }
  3014. if (nrf_cli_build_in_cmd_common_executed(p_cli, (argc != 2), NULL, 0))
  3015. {
  3016. return;
  3017. }
  3018. nrf_cli_fprintf(p_cli,
  3019. NRF_CLI_ERROR,
  3020. "%s:%s%s\r\n",
  3021. argv[0],
  3022. NRF_CLI_MSG_UNKNOWN_PARAMETER,
  3023. argv[1]);
  3024. }
  3025. #endif // NRF_MODULE_ENABLED(NRF_CLI_VT100_COLORS)
  3026. static void nrf_cli_cmd_echo(nrf_cli_t const * p_cli, size_t argc, char **argv)
  3027. {
  3028. if (nrf_cli_build_in_cmd_common_executed(p_cli, (argc > 2), NULL, 0))
  3029. {
  3030. return;
  3031. }
  3032. if (argc == 2)
  3033. {
  3034. nrf_cli_fprintf(p_cli,
  3035. NRF_CLI_ERROR,
  3036. "%s:%s%s\r\n",
  3037. argv[0],
  3038. NRF_CLI_MSG_UNKNOWN_PARAMETER,
  3039. argv[1]);
  3040. return;
  3041. }
  3042. nrf_cli_fprintf(p_cli,
  3043. NRF_CLI_NORMAL,
  3044. "Echo status: %s\r\n",
  3045. cli_flag_echo_is_set(p_cli) ? "on" : "off");
  3046. }
  3047. static void nrf_cli_cmd_echo_off(nrf_cli_t const * p_cli, size_t argc, char **argv)
  3048. {
  3049. if (nrf_cli_build_in_cmd_common_executed(p_cli, (argc != 1), NULL, 0))
  3050. {
  3051. return;
  3052. }
  3053. cli_flag_echo_clear(p_cli);
  3054. }
  3055. static void nrf_cli_cmd_echo_on(nrf_cli_t const * p_cli, size_t argc, char **argv)
  3056. {
  3057. if (nrf_cli_build_in_cmd_common_executed(p_cli, (argc != 1), NULL, 0))
  3058. {
  3059. return;
  3060. }
  3061. cli_flag_echo_set(p_cli);
  3062. }
  3063. #if NRF_MODULE_ENABLED(NRF_CLI_HISTORY)
  3064. static void nrf_cli_cmd_history(nrf_cli_t const * p_cli, size_t argc, char **argv)
  3065. {
  3066. ASSERT(p_cli);
  3067. ASSERT(p_cli->p_ctx && p_cli->p_iface && p_cli->p_name);
  3068. if (nrf_cli_build_in_cmd_common_executed(p_cli, (argc != 1), NULL, 0))
  3069. {
  3070. return;
  3071. }
  3072. size_t i = 0;
  3073. nrf_memobj_t const * p_cmd_list = p_cli->p_ctx->p_cmd_list_tail;
  3074. nrf_cli_memobj_header_t header;
  3075. while (1)
  3076. {
  3077. if ((p_cmd_list == NULL) || (i >= NRF_CLI_HISTORY_ELEMENT_COUNT))
  3078. {
  3079. break;
  3080. }
  3081. nrf_memobj_read((nrf_memobj_t * )p_cmd_list,
  3082. &header,
  3083. NRF_CLI_HISTORY_HEADER_SIZE,
  3084. 0);
  3085. nrf_memobj_read((nrf_memobj_t * )p_cmd_list,
  3086. p_cli->p_ctx->temp_buff,
  3087. header.cmd_len + 1,
  3088. NRF_CLI_HISTORY_HEADER_SIZE);
  3089. p_cmd_list = header.p_next;
  3090. nrf_cli_fprintf(p_cli, NRF_CLI_NORMAL, "[%3d] %s\r\n", i++, p_cli->p_ctx->temp_buff);
  3091. }
  3092. p_cli->p_ctx->temp_buff[0] = '\0';
  3093. }
  3094. #endif // NRF_MODULE_ENABLED(NRF_CLI_HISTORY)
  3095. #if NRF_MODULE_ENABLED(NRF_CLI_STATISTICS)
  3096. void nrf_cli_cmd_cli_stats(nrf_cli_t const * p_cli, size_t argc, char **argv)
  3097. {
  3098. if (argc == 1)
  3099. {
  3100. nrf_cli_help_print(p_cli, NULL, 0);
  3101. return;
  3102. }
  3103. if (argc == 2)
  3104. {
  3105. nrf_cli_fprintf(p_cli,
  3106. NRF_CLI_ERROR,
  3107. "%s:%s%s\r\n",
  3108. argv[0],
  3109. NRF_CLI_MSG_UNKNOWN_PARAMETER,
  3110. argv[1]);
  3111. return;
  3112. }
  3113. UNUSED_RETURN_VALUE(nrf_cli_build_in_cmd_common_executed(p_cli, (argc > 2), NULL, 0));
  3114. }
  3115. void nrf_cli_cmd_cli_stats_show(nrf_cli_t const * p_cli, size_t argc, char **argv)
  3116. {
  3117. if (nrf_cli_build_in_cmd_common_executed(p_cli, (argc != 1), NULL, 0))
  3118. {
  3119. return;
  3120. }
  3121. nrf_queue_t const * p_queue = ((nrf_cli_log_backend_t *)p_cli->p_log_backend->p_ctx)->p_queue;
  3122. uint8_t max_util = nrf_queue_max_utilization_get(p_queue);
  3123. uint8_t utilization = (uint8_t)(max_util * 100ul / p_queue->size);
  3124. nrf_cli_fprintf(p_cli,
  3125. NRF_CLI_NORMAL,
  3126. "Lost logs: %u\r\n"
  3127. "Max log queue utilization: %u%% [%u/%u]\r\n",
  3128. p_cli->p_ctx->statistics.log_lost_cnt,
  3129. utilization,
  3130. max_util,
  3131. p_queue->size);
  3132. }
  3133. void nrf_cli_cmd_cli_stats_reset(nrf_cli_t const * p_cli, size_t argc, char **argv)
  3134. {
  3135. if (nrf_cli_build_in_cmd_common_executed(p_cli, (argc != 1), NULL, 0))
  3136. {
  3137. return;
  3138. }
  3139. p_cli->p_ctx->statistics.log_lost_cnt = 0;
  3140. nrf_queue_max_utilization_reset(
  3141. ((nrf_cli_log_backend_t *)p_cli->p_log_backend->p_ctx)->p_queue);
  3142. }
  3143. #endif // NRF_MODULE_ENABLED(NRF_CLI_STATISTICS)
  3144. static void nrf_cli_cmd_resize_default(nrf_cli_t const * p_cli, size_t argc, char **argv)
  3145. {
  3146. if (nrf_cli_build_in_cmd_common_executed(p_cli, (argc != 1), NULL, 0))
  3147. {
  3148. return;
  3149. }
  3150. NRF_CLI_VT100_CMD(p_cli, NRF_CLI_VT100_SETCOL_80);
  3151. p_cli->p_ctx->vt100_ctx.cons.terminal_wid = NRF_CLI_DEFAULT_TERMINAL_WIDTH;
  3152. p_cli->p_ctx->vt100_ctx.cons.terminal_hei = NRF_CLI_DEFAULT_TERMINAL_HEIGHT;
  3153. }
  3154. static void nrf_cli_cmd_resize(nrf_cli_t const * p_cli, size_t argc, char **argv)
  3155. {
  3156. ASSERT(p_cli);
  3157. ASSERT(p_cli->p_ctx && p_cli->p_iface && p_cli->p_name);
  3158. if (argc == 1)
  3159. {
  3160. if (terminal_size_get(p_cli,
  3161. &p_cli->p_ctx->vt100_ctx.cons.terminal_wid,
  3162. &p_cli->p_ctx->vt100_ctx.cons.terminal_hei) != NRF_SUCCESS)
  3163. {
  3164. p_cli->p_ctx->vt100_ctx.cons.terminal_wid = NRF_CLI_DEFAULT_TERMINAL_WIDTH;
  3165. p_cli->p_ctx->vt100_ctx.cons.terminal_hei = NRF_CLI_DEFAULT_TERMINAL_HEIGHT;
  3166. nrf_cli_fprintf(p_cli,
  3167. NRF_CLI_WARNING,
  3168. "No response from the terminal, assumed 80x24 screen size\r\n");
  3169. }
  3170. return;
  3171. }
  3172. if (nrf_cli_build_in_cmd_common_executed(p_cli, (argc > 2), NULL, 0))
  3173. {
  3174. return;
  3175. }
  3176. nrf_cli_fprintf(p_cli,
  3177. NRF_CLI_ERROR,
  3178. "%s:%s%s\r\n",
  3179. argv[0],
  3180. NRF_CLI_MSG_UNKNOWN_PARAMETER,
  3181. argv[1]);
  3182. }
  3183. #if NRF_MODULE_ENABLED(NRF_CLI_VT100_COLORS)
  3184. NRF_CLI_CREATE_STATIC_SUBCMD_SET(m_sub_colors)
  3185. {
  3186. NRF_CLI_CMD(off, NULL, NRF_CLI_HELP_COLORS_OFF, nrf_cli_cmd_colors_off),
  3187. NRF_CLI_CMD(on, NULL, NRF_CLI_HELP_COLORS_ON, nrf_cli_cmd_colors_on),
  3188. NRF_CLI_SUBCMD_SET_END
  3189. };
  3190. #endif
  3191. NRF_CLI_CREATE_STATIC_SUBCMD_SET(m_sub_echo)
  3192. {
  3193. NRF_CLI_CMD(off, NULL, NRF_CLI_HELP_ECHO_OFF, nrf_cli_cmd_echo_off),
  3194. NRF_CLI_CMD(on, NULL, NRF_CLI_HELP_ECHO_ON, nrf_cli_cmd_echo_on),
  3195. NRF_CLI_SUBCMD_SET_END
  3196. };
  3197. #if NRF_MODULE_ENABLED(NRF_CLI_STATISTICS)
  3198. NRF_CLI_CREATE_STATIC_SUBCMD_SET(m_sub_cli_stats)
  3199. {
  3200. NRF_CLI_CMD(reset, NULL, NRF_CLI_HELP_STATISTICS_RESET, nrf_cli_cmd_cli_stats_reset),
  3201. NRF_CLI_CMD(show, NULL, NRF_CLI_HELP_STATISTICS_SHOW, nrf_cli_cmd_cli_stats_show),
  3202. NRF_CLI_SUBCMD_SET_END
  3203. };
  3204. #endif // NRF_MODULE_ENABLED(NRF_CLI_STATISTICS)
  3205. NRF_CLI_CREATE_STATIC_SUBCMD_SET(m_sub_cli)
  3206. {
  3207. #if NRF_MODULE_ENABLED(NRF_CLI_VT100_COLORS)
  3208. NRF_CLI_CMD(colors, &m_sub_colors, NRF_CLI_HELP_COLORS, nrf_cli_cmd_colors),
  3209. #endif
  3210. NRF_CLI_CMD(echo, &m_sub_echo, NRF_CLI_HELP_ECHO, nrf_cli_cmd_echo),
  3211. #if NRF_MODULE_ENABLED(NRF_CLI_STATISTICS)
  3212. NRF_CLI_CMD(stats, &m_sub_cli_stats, NRF_CLI_HELP_STATISTICS, nrf_cli_cmd_cli_stats),
  3213. #endif
  3214. NRF_CLI_SUBCMD_SET_END
  3215. };
  3216. NRF_CLI_CREATE_STATIC_SUBCMD_SET(m_sub_resize)
  3217. {
  3218. NRF_CLI_CMD(default, NULL, NRF_CLI_HELP_RESIZE_DEFAULT, nrf_cli_cmd_resize_default),
  3219. NRF_CLI_SUBCMD_SET_END
  3220. };
  3221. NRF_CLI_CMD_REGISTER(clear, NULL, NRF_CLI_HELP_CLEAR, nrf_cli_cmd_clear);
  3222. NRF_CLI_CMD_REGISTER(cli, &m_sub_cli, NRF_CLI_HELP_CLI, nrf_cli_cmd_cli);
  3223. #if NRF_MODULE_ENABLED(NRF_CLI_HISTORY)
  3224. NRF_CLI_CMD_REGISTER(history, NULL, NRF_CLI_HELP_HISTORY, nrf_cli_cmd_history);
  3225. #endif
  3226. NRF_CLI_CMD_REGISTER(resize, &m_sub_resize, NRF_CLI_HELP_RESIZE, nrf_cli_cmd_resize);
  3227. #endif // NRF_MODULE_ENABLED(NRF_CLI_BUILD_IN_CMDS)
  3228. #endif // NRF_MODULE_ENABLED(NRF_CLI)