vi.c 152 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496249724982499250025012502250325042505250625072508250925102511251225132514251525162517251825192520252125222523252425252526252725282529253025312532253325342535253625372538253925402541254225432544254525462547254825492550255125522553255425552556255725582559256025612562256325642565256625672568256925702571257225732574257525762577257825792580258125822583258425852586258725882589259025912592259325942595259625972598259926002601260226032604260526062607260826092610261126122613261426152616261726182619262026212622262326242625262626272628262926302631263226332634263526362637263826392640264126422643264426452646264726482649265026512652265326542655265626572658265926602661266226632664266526662667266826692670267126722673267426752676267726782679268026812682268326842685268626872688268926902691269226932694269526962697269826992700270127022703270427052706270727082709271027112712271327142715271627172718271927202721272227232724272527262727272827292730273127322733273427352736273727382739274027412742274327442745274627472748274927502751275227532754275527562757275827592760276127622763276427652766276727682769277027712772277327742775277627772778277927802781278227832784278527862787278827892790279127922793279427952796279727982799280028012802280328042805280628072808280928102811281228132814281528162817281828192820282128222823282428252826282728282829283028312832283328342835283628372838283928402841284228432844284528462847284828492850285128522853285428552856285728582859286028612862286328642865286628672868286928702871287228732874287528762877287828792880288128822883288428852886288728882889289028912892289328942895289628972898289929002901290229032904290529062907290829092910291129122913291429152916291729182919292029212922292329242925292629272928292929302931293229332934293529362937293829392940294129422943294429452946294729482949295029512952295329542955295629572958295929602961296229632964296529662967296829692970297129722973297429752976297729782979298029812982298329842985298629872988298929902991299229932994299529962997299829993000300130023003300430053006300730083009301030113012301330143015301630173018301930203021302230233024302530263027302830293030303130323033303430353036303730383039304030413042304330443045304630473048304930503051305230533054305530563057305830593060306130623063306430653066306730683069307030713072307330743075307630773078307930803081308230833084308530863087308830893090309130923093309430953096309730983099310031013102310331043105310631073108310931103111311231133114311531163117311831193120312131223123312431253126312731283129313031313132313331343135313631373138313931403141314231433144314531463147314831493150315131523153315431553156315731583159316031613162316331643165316631673168316931703171317231733174317531763177317831793180318131823183318431853186318731883189319031913192319331943195319631973198319932003201320232033204320532063207320832093210321132123213321432153216321732183219322032213222322332243225322632273228322932303231323232333234323532363237323832393240324132423243324432453246324732483249325032513252325332543255325632573258325932603261326232633264326532663267326832693270327132723273327432753276327732783279328032813282328332843285328632873288328932903291329232933294329532963297329832993300330133023303330433053306330733083309331033113312331333143315331633173318331933203321332233233324332533263327332833293330333133323333333433353336333733383339334033413342334333443345334633473348334933503351335233533354335533563357335833593360336133623363336433653366336733683369337033713372337333743375337633773378337933803381338233833384338533863387338833893390339133923393339433953396339733983399340034013402340334043405340634073408340934103411341234133414341534163417341834193420342134223423342434253426342734283429343034313432343334343435343634373438343934403441344234433444344534463447344834493450345134523453345434553456345734583459346034613462346334643465346634673468346934703471347234733474347534763477347834793480348134823483348434853486348734883489349034913492349334943495349634973498349935003501350235033504350535063507350835093510351135123513351435153516351735183519352035213522352335243525352635273528352935303531353235333534353535363537353835393540354135423543354435453546354735483549355035513552355335543555355635573558355935603561356235633564356535663567356835693570357135723573357435753576357735783579358035813582358335843585358635873588358935903591359235933594359535963597359835993600360136023603360436053606360736083609361036113612361336143615361636173618361936203621362236233624362536263627362836293630363136323633363436353636363736383639364036413642364336443645364636473648364936503651365236533654365536563657365836593660366136623663366436653666366736683669367036713672367336743675367636773678367936803681368236833684368536863687368836893690369136923693369436953696369736983699370037013702370337043705370637073708370937103711371237133714371537163717371837193720372137223723372437253726372737283729373037313732373337343735373637373738373937403741374237433744374537463747374837493750375137523753375437553756375737583759376037613762376337643765376637673768376937703771377237733774377537763777377837793780378137823783378437853786378737883789379037913792379337943795379637973798379938003801380238033804380538063807380838093810381138123813381438153816381738183819382038213822382338243825382638273828382938303831383238333834383538363837383838393840384138423843384438453846384738483849385038513852385338543855385638573858385938603861386238633864386538663867386838693870387138723873387438753876387738783879388038813882388338843885388638873888388938903891389238933894389538963897389838993900390139023903390439053906390739083909391039113912391339143915391639173918391939203921392239233924392539263927392839293930393139323933393439353936393739383939394039413942394339443945394639473948394939503951395239533954395539563957395839593960396139623963396439653966396739683969397039713972397339743975397639773978397939803981398239833984398539863987398839893990399139923993399439953996399739983999400040014002400340044005400640074008400940104011401240134014401540164017401840194020402140224023402440254026402740284029403040314032403340344035403640374038403940404041404240434044404540464047404840494050405140524053405440554056405740584059406040614062406340644065406640674068406940704071407240734074407540764077407840794080408140824083408440854086408740884089409040914092409340944095409640974098409941004101410241034104410541064107410841094110411141124113411441154116411741184119412041214122412341244125412641274128412941304131413241334134413541364137413841394140414141424143414441454146414741484149415041514152415341544155415641574158415941604161416241634164416541664167416841694170417141724173417441754176417741784179418041814182418341844185418641874188418941904191419241934194419541964197419841994200420142024203420442054206420742084209421042114212421342144215421642174218421942204221422242234224422542264227422842294230423142324233423442354236423742384239424042414242424342444245424642474248424942504251425242534254425542564257425842594260426142624263426442654266426742684269427042714272427342744275427642774278427942804281428242834284428542864287428842894290429142924293429442954296429742984299430043014302430343044305430643074308430943104311431243134314431543164317431843194320432143224323432443254326432743284329433043314332433343344335433643374338433943404341434243434344434543464347434843494350435143524353435443554356435743584359436043614362436343644365436643674368436943704371437243734374437543764377437843794380438143824383438443854386438743884389439043914392439343944395439643974398439944004401440244034404440544064407440844094410441144124413441444154416441744184419442044214422442344244425442644274428442944304431443244334434443544364437443844394440444144424443444444454446444744484449445044514452445344544455445644574458445944604461446244634464446544664467446844694470447144724473447444754476447744784479448044814482448344844485448644874488448944904491449244934494449544964497449844994500450145024503450445054506450745084509451045114512451345144515451645174518451945204521452245234524452545264527452845294530453145324533453445354536453745384539454045414542454345444545454645474548454945504551455245534554455545564557455845594560456145624563456445654566456745684569457045714572457345744575457645774578457945804581458245834584458545864587458845894590459145924593459445954596459745984599460046014602460346044605460646074608460946104611461246134614461546164617461846194620462146224623462446254626462746284629463046314632463346344635463646374638463946404641464246434644464546464647464846494650465146524653465446554656465746584659466046614662466346644665466646674668466946704671467246734674467546764677467846794680468146824683468446854686468746884689469046914692469346944695469646974698469947004701470247034704470547064707470847094710471147124713471447154716471747184719472047214722472347244725472647274728472947304731473247334734473547364737473847394740474147424743474447454746474747484749475047514752475347544755475647574758475947604761476247634764476547664767476847694770477147724773477447754776477747784779478047814782478347844785478647874788478947904791479247934794479547964797479847994800480148024803480448054806480748084809481048114812481348144815481648174818481948204821482248234824482548264827482848294830483148324833483448354836483748384839484048414842484348444845484648474848484948504851485248534854485548564857485848594860486148624863486448654866486748684869487048714872487348744875487648774878487948804881488248834884488548864887488848894890489148924893489448954896489748984899490049014902490349044905490649074908490949104911491249134914491549164917491849194920492149224923492449254926492749284929493049314932493349344935493649374938493949404941494249434944494549464947494849494950495149524953495449554956495749584959496049614962
  1. /* vi: set sw=4 ts=4: */
  2. /*
  3. * tiny vi.c: A small 'vi' clone
  4. * Copyright (C) 2000, 2001 Sterling Huxley <sterling@europa.com>
  5. *
  6. * Licensed under GPLv2 or later, see file LICENSE in this source tree.
  7. */
  8. //'vi' is a text editor. More specifically, it is the One True
  9. //text editor <grin>. It does, however, have a rather steep
  10. //learning curve. If you are not already comfortable with 'vi'
  11. //you may wish to use something else.
  12. #include <rtthread.h>
  13. #include <optparse.h>
  14. #include "vi_utils.h"
  15. /* This struct is deliberately not defined. */
  16. /* See docs/keep_data_small.txt */
  17. struct globals;
  18. /* '*const' ptr makes gcc optimize code much better.
  19. * Magic prevents ptr_to_globals from going into rodata.
  20. * If you want to assign a value, use SET_PTR_TO_GLOBALS(x) */
  21. struct globals *ptr_to_globals;
  22. /* Should be after libbb.h: on some systems regex.h needs sys/types.h: */
  23. #if ENABLE_FEATURE_VI_REGEX_SEARCH
  24. # include <regex.h>
  25. #endif
  26. /* the CRASHME code is unmaintained, and doesn't currently build */
  27. #define ENABLE_FEATURE_VI_CRASHME 0
  28. #if ENABLE_LOCALE_SUPPORT
  29. #if ENABLE_FEATURE_VI_8BIT
  30. //FIXME: this does not work properly for Unicode anyway
  31. # define Isprint(c) (isprint)(c)
  32. #else
  33. # define Isprint(c) isprint_asciionly(c)
  34. #endif
  35. #else
  36. /* 0x9b is Meta-ESC */
  37. #if ENABLE_FEATURE_VI_8BIT
  38. # define Isprint(c) ((unsigned char)(c) >= ' ' && (c) != 0x7f && (unsigned char)(c) != 0x9b)
  39. #else
  40. # define Isprint(c) ((unsigned char)(c) >= ' ' && (unsigned char)(c) < 0x7f)
  41. #endif
  42. #endif
  43. enum {
  44. MAX_TABSTOP = 32, // sanity limit
  45. // User input len. Need not be extra big.
  46. // Lines in file being edited *can* be bigger than this.
  47. MAX_INPUT_LEN = 128,
  48. // Sanity limits. We have only one buffer of this size.
  49. MAX_SCR_COLS = CONFIG_FEATURE_VI_MAX_LEN,
  50. MAX_SCR_ROWS = CONFIG_FEATURE_VI_MAX_LEN,
  51. };
  52. // VT102 ESC sequences.
  53. // See "Xterm Control Sequences"
  54. // http://invisible-island.net/xterm/ctlseqs/ctlseqs.html
  55. #define ESC "\033"
  56. // Inverse/Normal text
  57. #define ESC_BOLD_TEXT ESC"[7m"
  58. #define ESC_NORM_TEXT ESC"[m"
  59. // Bell
  60. #define ESC_BELL "\007"
  61. // Clear-to-end-of-line
  62. #define ESC_CLEAR2EOL ESC"[K"
  63. // Clear-to-end-of-screen.
  64. // (We use default param here.
  65. // Full sequence is "ESC [ <num> J",
  66. // <num> is 0/1/2 = "erase below/above/all".)
  67. #define ESC_CLEAR2EOS ESC"[J"
  68. // Cursor to given coordinate (1,1: top left)
  69. #define ESC_SET_CURSOR_POS ESC"[%u;%uH"
  70. #define ESC_SET_CURSOR_TOPLEFT ESC"[H"
  71. //UNUSED
  72. //// Cursor up and down
  73. //#define ESC_CURSOR_UP ESC"[A"
  74. //#define ESC_CURSOR_DOWN "\n"
  75. #if ENABLE_FEATURE_VI_DOT_CMD
  76. static const char modifying_cmds[] ALIGN1 = "aAcCdDiIJoOpPrRs""xX<>~";
  77. #endif
  78. enum {
  79. YANKONLY = FALSE,
  80. YANKDEL = TRUE,
  81. FORWARD = 1, // code depends on "1" for array index
  82. BACK = -1, // code depends on "-1" for array index
  83. LIMITED = 0, // char_search() only current line
  84. FULL = 1, // char_search() to the end/beginning of entire text
  85. PARTIAL = 0, // buffer contains partial line
  86. WHOLE = 1, // buffer contains whole lines
  87. MULTI = 2, // buffer may include newlines
  88. S_BEFORE_WS = 1, // used in skip_thing() for moving "dot"
  89. S_TO_WS = 2, // used in skip_thing() for moving "dot"
  90. S_OVER_WS = 3, // used in skip_thing() for moving "dot"
  91. S_END_PUNCT = 4, // used in skip_thing() for moving "dot"
  92. S_END_ALNUM = 5, // used in skip_thing() for moving "dot"
  93. C_END = -1, // cursor is at end of line due to '$' command
  94. };
  95. /* vi.c expects chars to be unsigned. */
  96. /* busybox build system provides that, but it's better */
  97. /* to audit and fix the source */
  98. struct globals {
  99. /* many references - keep near the top of globals */
  100. char *text, *end; // pointers to the user data in memory
  101. char *dot; // where all the action takes place
  102. int text_size; // size of the allocated buffer
  103. // the rest
  104. #if ENABLE_FEATURE_VI_SETOPTS
  105. smallint vi_setops; // set by setops()
  106. #define VI_AUTOINDENT (1 << 0)
  107. #define VI_EXPANDTAB (1 << 1)
  108. #define VI_ERR_METHOD (1 << 2)
  109. #define VI_IGNORECASE (1 << 3)
  110. #define VI_SHOWMATCH (1 << 4)
  111. #define VI_TABSTOP (1 << 5)
  112. #define autoindent (vi_setops & VI_AUTOINDENT)
  113. #define expandtab (vi_setops & VI_EXPANDTAB )
  114. #define err_method (vi_setops & VI_ERR_METHOD) // indicate error with beep or flash
  115. #define ignorecase (vi_setops & VI_IGNORECASE)
  116. #define showmatch (vi_setops & VI_SHOWMATCH )
  117. // order of constants and strings must match
  118. #define OPTS_STR \
  119. "ai\0""autoindent\0" \
  120. "et\0""expandtab\0" \
  121. "fl\0""flash\0" \
  122. "ic\0""ignorecase\0" \
  123. "sm\0""showmatch\0" \
  124. "ts\0""tabstop\0"
  125. #else
  126. #define autoindent (0)
  127. #define expandtab (0)
  128. #define err_method (0)
  129. #define ignorecase (0)
  130. #endif
  131. #if ENABLE_FEATURE_VI_READONLY
  132. smallint readonly_mode;
  133. #define SET_READONLY_FILE(flags) ((flags) |= 0x01)
  134. #define SET_READONLY_MODE(flags) ((flags) |= 0x02)
  135. #define UNSET_READONLY_FILE(flags) ((flags) &= 0xfe)
  136. #else
  137. #define SET_READONLY_FILE(flags) ((void)0)
  138. #define SET_READONLY_MODE(flags) ((void)0)
  139. #define UNSET_READONLY_FILE(flags) ((void)0)
  140. #endif
  141. smallint editing; // >0 while we are editing a file
  142. // [code audit says "can be 0, 1 or 2 only"]
  143. smallint cmd_mode; // 0=command 1=insert 2=replace
  144. int modified_count; // buffer contents changed if !0
  145. int last_modified_count; // = -1;
  146. int cmdline_filecnt; // how many file names on cmd line
  147. int cmdcnt; // repetition count
  148. unsigned rows, columns; // the terminal screen is this size
  149. #if ENABLE_FEATURE_VI_ASK_TERMINAL
  150. int get_rowcol_error;
  151. #endif
  152. int crow, ccol; // cursor is on Crow x Ccol
  153. int offset; // chars scrolled off the screen to the left
  154. int have_status_msg; // is default edit status needed?
  155. // [don't make smallint!]
  156. int last_status_cksum; // hash of current status line
  157. char *current_filename;
  158. #if ENABLE_FEATURE_VI_COLON_EXPAND
  159. char *alt_filename;
  160. #endif
  161. char *screenbegin; // index into text[], of top line on the screen
  162. char *screen; // pointer to the virtual screen buffer
  163. int screensize; // and its size
  164. int tabstop;
  165. int last_search_char; // last char searched for (int because of Unicode)
  166. smallint last_search_cmd; // command used to invoke last char search
  167. #if ENABLE_FEATURE_VI_CRASHME
  168. char last_input_char; // last char read from user
  169. #endif
  170. #if ENABLE_FEATURE_VI_UNDO_QUEUE
  171. char undo_queue_state; // One of UNDO_INS, UNDO_DEL, UNDO_EMPTY
  172. #endif
  173. #if ENABLE_FEATURE_VI_DOT_CMD
  174. smallint adding2q; // are we currently adding user input to q
  175. int lmc_len; // length of last_modifying_cmd
  176. char *ioq, *ioq_start; // pointer to string for get_one_char to "read"
  177. int dotcnt; // number of times to repeat '.' command
  178. #endif
  179. #if ENABLE_FEATURE_VI_SEARCH
  180. char *last_search_pattern; // last pattern from a '/' or '?' search
  181. #endif
  182. #if ENABLE_FEATURE_VI_SETOPTS
  183. int indentcol; // column of recently autoindent, 0 or -1
  184. #endif
  185. smallint cmd_error;
  186. /* former statics */
  187. #if ENABLE_FEATURE_VI_YANKMARK
  188. char *edit_file__cur_line;
  189. #endif
  190. int refresh__old_offset;
  191. int format_edit_status__tot;
  192. /* a few references only */
  193. #if ENABLE_FEATURE_VI_YANKMARK
  194. smalluint YDreg;//,Ureg;// default delete register and orig line for "U"
  195. #define Ureg 27
  196. char *reg[28]; // named register a-z, "D", and "U" 0-25,26,27
  197. char regtype[28]; // buffer type: WHOLE, MULTI or PARTIAL
  198. char *mark[28]; // user marks points somewhere in text[]- a-z and previous context ''
  199. #endif
  200. #if ENABLE_FEATURE_VI_USE_SIGNALS
  201. sigjmp_buf restart; // int_handler() jumps to location remembered here
  202. #endif
  203. #ifdef RT_USING_POSIX_TERMIOS // RT-Thread team added
  204. struct termios term_orig; // remember what the cooked mode was
  205. #endif
  206. int cindex; // saved character index for up/down motion
  207. smallint keep_index; // retain saved character index
  208. #if ENABLE_FEATURE_VI_COLON
  209. char *initial_cmds[3]; // currently 2 entries, NULL terminated
  210. #endif
  211. // Should be just enough to hold a key sequence,
  212. // but CRASHME mode uses it as generated command buffer too
  213. #if ENABLE_FEATURE_VI_CRASHME
  214. char readbuffer[128];
  215. #else
  216. char readbuffer[KEYCODE_BUFFER_SIZE];
  217. #endif
  218. #define STATUS_BUFFER_LEN 200
  219. char status_buffer[STATUS_BUFFER_LEN]; // messages to the user
  220. #if ENABLE_FEATURE_VI_DOT_CMD
  221. char last_modifying_cmd[MAX_INPUT_LEN]; // last modifying cmd for "."
  222. #endif
  223. char get_input_line__buf[MAX_INPUT_LEN]; /* former static */
  224. char scr_out_buf[MAX_SCR_COLS + MAX_TABSTOP * 2];
  225. #if ENABLE_FEATURE_VI_UNDO
  226. // undo_push() operations
  227. #define UNDO_INS 0
  228. #define UNDO_DEL 1
  229. #define UNDO_INS_CHAIN 2
  230. #define UNDO_DEL_CHAIN 3
  231. # if ENABLE_FEATURE_VI_UNDO_QUEUE
  232. #define UNDO_INS_QUEUED 4
  233. #define UNDO_DEL_QUEUED 5
  234. # endif
  235. // Pass-through flags for functions that can be undone
  236. #define NO_UNDO 0
  237. #define ALLOW_UNDO 1
  238. #define ALLOW_UNDO_CHAIN 2
  239. # if ENABLE_FEATURE_VI_UNDO_QUEUE
  240. #define ALLOW_UNDO_QUEUED 3
  241. # else
  242. // If undo queuing disabled, don't invoke the missing queue logic
  243. #define ALLOW_UNDO_QUEUED ALLOW_UNDO
  244. # endif
  245. struct undo_object {
  246. struct undo_object *prev; // Linking back avoids list traversal (LIFO)
  247. int start; // Offset where the data should be restored/deleted
  248. int length; // total data size
  249. uint8_t u_type; // 0=deleted, 1=inserted, 2=swapped
  250. char undo_text[1]; // text that was deleted (if deletion)
  251. } *undo_stack_tail;
  252. # if ENABLE_FEATURE_VI_UNDO_QUEUE
  253. #define UNDO_USE_SPOS 32
  254. #define UNDO_EMPTY 64
  255. char *undo_queue_spos; // Start position of queued operation
  256. int undo_q;
  257. char undo_queue[CONFIG_FEATURE_VI_UNDO_QUEUE_MAX];
  258. # endif
  259. #endif /* ENABLE_FEATURE_VI_UNDO */
  260. };
  261. #define G (*ptr_to_globals)
  262. #define text (G.text )
  263. #define text_size (G.text_size )
  264. #define end (G.end )
  265. #define dot (G.dot )
  266. #define reg (G.reg )
  267. #define vi_setops (G.vi_setops )
  268. #define editing (G.editing )
  269. #define cmd_mode (G.cmd_mode )
  270. #define modified_count (G.modified_count )
  271. #define last_modified_count (G.last_modified_count)
  272. #define cmdline_filecnt (G.cmdline_filecnt )
  273. #define cmdcnt (G.cmdcnt )
  274. #define rows (G.rows )
  275. #define columns (G.columns )
  276. #define crow (G.crow )
  277. #define ccol (G.ccol )
  278. #define offset (G.offset )
  279. #define status_buffer (G.status_buffer )
  280. #define have_status_msg (G.have_status_msg )
  281. #define last_status_cksum (G.last_status_cksum )
  282. #define current_filename (G.current_filename )
  283. #define alt_filename (G.alt_filename )
  284. #define screen (G.screen )
  285. #define screensize (G.screensize )
  286. #define screenbegin (G.screenbegin )
  287. #define tabstop (G.tabstop )
  288. #define last_search_char (G.last_search_char )
  289. #define last_search_cmd (G.last_search_cmd )
  290. #if ENABLE_FEATURE_VI_CRASHME
  291. #define last_input_char (G.last_input_char )
  292. #endif
  293. #if ENABLE_FEATURE_VI_READONLY
  294. #define readonly_mode (G.readonly_mode )
  295. #else
  296. #define readonly_mode 0
  297. #endif
  298. #define adding2q (G.adding2q )
  299. #define lmc_len (G.lmc_len )
  300. #define ioq (G.ioq )
  301. #define ioq_start (G.ioq_start )
  302. #define dotcnt (G.dotcnt )
  303. #define last_search_pattern (G.last_search_pattern)
  304. #define indentcol (G.indentcol )
  305. #define cmd_error (G.cmd_error )
  306. #define edit_file__cur_line (G.edit_file__cur_line)
  307. #define refresh__old_offset (G.refresh__old_offset)
  308. #define format_edit_status__tot (G.format_edit_status__tot)
  309. #define YDreg (G.YDreg )
  310. //#define Ureg (G.Ureg )
  311. #define regtype (G.regtype )
  312. #define mark (G.mark )
  313. #define restart (G.restart )
  314. #ifdef RT_USING_POSIX_TERMIOS // RT-Thread team added
  315. #define term_orig (G.term_orig )
  316. #endif
  317. #define cindex (G.cindex )
  318. #define keep_index (G.keep_index )
  319. #define initial_cmds (G.initial_cmds )
  320. #define readbuffer (G.readbuffer )
  321. #define scr_out_buf (G.scr_out_buf )
  322. #define last_modifying_cmd (G.last_modifying_cmd )
  323. #define get_input_line__buf (G.get_input_line__buf)
  324. #if ENABLE_FEATURE_VI_UNDO
  325. #define undo_stack_tail (G.undo_stack_tail )
  326. # if ENABLE_FEATURE_VI_UNDO_QUEUE
  327. #define undo_queue_state (G.undo_queue_state)
  328. #define undo_q (G.undo_q )
  329. #define undo_queue (G.undo_queue )
  330. #define undo_queue_spos (G.undo_queue_spos )
  331. # endif
  332. #endif
  333. #define INIT_G() do { \
  334. SET_PTR_TO_GLOBALS(vi_zalloc(sizeof(G))); \
  335. last_modified_count = -1; \
  336. /* "" but has space for 2 chars: */ \
  337. IF_FEATURE_VI_SEARCH(last_search_pattern = vi_zalloc(2);) \
  338. tabstop = 8; \
  339. } while (0)
  340. static void edit_file(char *); // edit one file
  341. static void do_cmd(int); // execute a command
  342. static int next_tabstop(int);
  343. static void sync_cursor(char *, int *, int *); // synchronize the screen cursor to dot
  344. static char *begin_line(char *); // return pointer to cur line B-o-l
  345. static char *end_line(char *); // return pointer to cur line E-o-l
  346. static char *prev_line(char *); // return pointer to prev line B-o-l
  347. static char *next_line(char *); // return pointer to next line B-o-l
  348. static char *end_screen(void); // get pointer to last char on screen
  349. static int count_lines(char *, char *); // count line from start to stop
  350. static char *find_line(int); // find begining of line #li
  351. static char *move_to_col(char *, int); // move "p" to column l
  352. static void dot_left(void); // move dot left- dont leave line
  353. static void dot_right(void); // move dot right- dont leave line
  354. static void dot_begin(void); // move dot to B-o-l
  355. static void dot_end(void); // move dot to E-o-l
  356. static void dot_next(void); // move dot to next line B-o-l
  357. static void dot_prev(void); // move dot to prev line B-o-l
  358. static void dot_scroll(int, int); // move the screen up or down
  359. static void dot_skip_over_ws(void); // move dot pat WS
  360. static char *bound_dot(char *); // make sure text[0] <= P < "end"
  361. static void new_screen(int, int); // malloc virtual screen memory
  362. #if !ENABLE_FEATURE_VI_UNDO
  363. #define char_insert(a,b,c) char_insert(a,b)
  364. #endif
  365. static char *char_insert(char *, char, int); // insert the char c at 'p'
  366. // might reallocate text[]! use p += stupid_insert(p, ...),
  367. // and be careful to not use pointers into potentially freed text[]!
  368. static uintptr_t stupid_insert(char *, char); // stupidly insert the char c at 'p'
  369. static int find_range(char **start, char **stop, int cmd); // return pointers for an object
  370. static int st_test(char *, int, int, char *); // helper for skip_thing()
  371. static char *skip_thing(char *, int, int, int); // skip some object
  372. static char *find_pair(char *, const char); // find matching pair () [] {}
  373. #if !ENABLE_FEATURE_VI_UNDO
  374. #define text_hole_delete(a,b,c) text_hole_delete(a,b)
  375. #endif
  376. static char *text_hole_delete(char *, char *, int); // at "p", delete a 'size' byte hole
  377. // might reallocate text[]! use p += text_hole_make(p, ...),
  378. // and be careful to not use pointers into potentially freed text[]!
  379. static uintptr_t text_hole_make(char *, int); // at "p", make a 'size' byte hole
  380. #if !ENABLE_FEATURE_VI_UNDO
  381. #define yank_delete(a,b,c,d,e) yank_delete(a,b,c,d)
  382. #endif
  383. static char *yank_delete(char *, char *, int, int, int); // yank text[] into register then delete
  384. static void show_help(void); // display some help info
  385. static void rawmode(void); // set "raw" mode on tty
  386. static void cookmode(void); // return to "cooked" mode on tty
  387. // sleep for 'h' 1/100 seconds, return 1/0 if stdin is (ready for read)/(not ready)
  388. static int mysleep(int);
  389. static int readit(void);
  390. #if ENABLE_FEATURE_VI_DOT_CMD
  391. static int get_one_char(void); // read 1 char from stdin
  392. #else
  393. # define get_one_char() readit()
  394. #endif
  395. static int get_motion_char(void); // Get type of thing to operate on and adjust count
  396. static int file_insert(const char *, char *, int); // file_insert might reallocate text[]!
  397. static int file_write(char *, char *, char *);
  398. static void screen_erase(void);
  399. static void go_bottom_and_clear_to_eol(void);
  400. static void standout_start(void); // send "start reverse video" sequence
  401. static void standout_end(void); // send "end reverse video" sequence
  402. static void flash(int); // flash the terminal screen
  403. static void show_status_line(void); // put a message on the bottom line
  404. static void status_line(const char *, ...); // print to status buf
  405. static void status_line_bold(const char *, ...);
  406. static void status_line_bold_errno(const char *fn);
  407. static void not_implemented(const char *); // display "Not implemented" message
  408. static int format_edit_status(void); // format file status on status line
  409. static void redraw(int); // force a full screen refresh
  410. static char *format_line(char* /*, int*/);
  411. static void refresh(int); // update the terminal from screen[]
  412. static void indicate_error(void); // use flash or beep to indicate error
  413. static void Hit_Return(void);
  414. #if ENABLE_FEATURE_VI_SEARCH
  415. static char *char_search(char *, const char *, int); // search for pattern starting at p
  416. #endif
  417. #if ENABLE_FEATURE_VI_COLON
  418. static char *get_address(char *p, int *b, int *e, unsigned int *got); // get colon addr, if present
  419. #endif
  420. static void colon(char *); // execute the "colon" mode cmds
  421. #if ENABLE_FEATURE_VI_USE_SIGNALS
  422. static void winch_handler(int); // catch window size changes
  423. static void tstp_handler(int); // catch ctrl-Z
  424. static void int_handler(int); // catch ctrl-C
  425. #endif
  426. #if ENABLE_FEATURE_VI_DOT_CMD
  427. static void start_new_cmd_q(char); // new queue for command
  428. static void end_cmd_q(void); // stop saving input chars
  429. #else
  430. #define end_cmd_q() ((void)0)
  431. #endif
  432. #if ENABLE_FEATURE_VI_SETOPTS
  433. static void showmatching(char *); // show the matching pair () [] {}
  434. #endif
  435. #if ENABLE_FEATURE_VI_YANKMARK || (ENABLE_FEATURE_VI_COLON && ENABLE_FEATURE_VI_SEARCH) || ENABLE_FEATURE_VI_CRASHME
  436. // might reallocate text[]! use p += string_insert(p, ...),
  437. // and be careful to not use pointers into potentially freed text[]!
  438. # if !ENABLE_FEATURE_VI_UNDO
  439. #define string_insert(a,b,c) string_insert(a,b)
  440. # endif
  441. static uintptr_t string_insert(char *, const char *, int); // insert the string at 'p'
  442. #endif
  443. #if ENABLE_FEATURE_VI_YANKMARK
  444. static char *text_yank(char *, char *, int, int); // save copy of "p" into a register
  445. static char what_reg(void); // what is letter of current YDreg
  446. static void check_context(char); // remember context for '' command
  447. #endif
  448. #if ENABLE_FEATURE_VI_UNDO
  449. static void flush_undo_data(void);
  450. static void undo_push(char *, unsigned, int); // push an operation on the undo stack
  451. static void undo_push_insert(char *, int, int); // convenience function
  452. static void undo_pop(void); // undo the last operation
  453. # if ENABLE_FEATURE_VI_UNDO_QUEUE
  454. static void undo_queue_commit(void); // flush any queued objects to the undo stack
  455. # else
  456. # define undo_queue_commit() ((void)0)
  457. # endif
  458. #else
  459. #define flush_undo_data() ((void)0)
  460. #define undo_queue_commit() ((void)0)
  461. #endif
  462. #if ENABLE_FEATURE_VI_CRASHME
  463. static void crash_dummy();
  464. static void crash_test();
  465. static int crashme = 0;
  466. #endif
  467. static struct optparse options;
  468. static void write1(const char *out)
  469. {
  470. rt_kprintf(out); /* fputs(out, stdout); */
  471. }
  472. static int vi_main(int argc, char **argv)
  473. {
  474. int c;
  475. char *file_name;
  476. if(vi_mem_init() == 0) // RT-Thread team added
  477. {
  478. rt_kprintf("vi initialization failed.\r\n");
  479. return -1;
  480. }
  481. INIT_G();
  482. #if ENABLE_FEATURE_VI_UNDO
  483. /* undo_stack_tail = NULL; - already is */
  484. #if ENABLE_FEATURE_VI_UNDO_QUEUE
  485. undo_queue_state = UNDO_EMPTY;
  486. /* undo_q = 0; - already is */
  487. #endif
  488. #endif
  489. #if ENABLE_FEATURE_VI_CRASHME
  490. srand((long) getpid());
  491. #endif
  492. #ifdef NO_SUCH_APPLET_YET
  493. // if we aren't "vi", we are "view"
  494. if (ENABLE_FEATURE_VI_READONLY && applet_name[2]) {
  495. SET_READONLY_MODE(readonly_mode);
  496. }
  497. #endif
  498. // 0: all of our options are disabled by default in vim
  499. //vi_setops = 0;
  500. // 1- process EXINIT variable from environment
  501. // 2- if EXINIT is unset process $HOME/.exrc file (not inplemented yet)
  502. // 3- process command line args
  503. #if ENABLE_FEATURE_VI_COLON
  504. {
  505. char *p = getenv("EXINIT");
  506. if (p && *p)
  507. initial_cmds[0] = vi_strndup(p, MAX_INPUT_LEN);
  508. }
  509. #endif
  510. optparse_init(&options, argv); // RT-Thread team added
  511. while ((c = optparse(&options,
  512. #if ENABLE_FEATURE_VI_CRASHME
  513. "C"
  514. #endif
  515. "RHh" IF_FEATURE_VI_COLON("c:"))) != -1) {
  516. switch (c) {
  517. #if ENABLE_FEATURE_VI_CRASHME
  518. case 'C':
  519. crashme = 1;
  520. break;
  521. #endif
  522. #if ENABLE_FEATURE_VI_READONLY
  523. case 'R': // Read-only flag
  524. SET_READONLY_MODE(readonly_mode);
  525. break;
  526. #endif
  527. #if ENABLE_FEATURE_VI_COLON
  528. case 'c': // cmd line vi command
  529. if (*options.optarg)
  530. initial_cmds[initial_cmds[0] != NULL] = vi_strndup(options.optarg, MAX_INPUT_LEN);
  531. break;
  532. #endif
  533. case 'H':
  534. show_help();
  535. // fall through
  536. default:
  537. rt_kprintf("Usage: vi [FILE]\n"); /*bb_show_usage();*/
  538. goto vi_exit;
  539. }
  540. }
  541. options.argv += options.optind;
  542. cmdline_filecnt = argc - options.optind;
  543. options.optind = 0;
  544. file_name = optparse_arg(&options);
  545. if(file_name == NULL)
  546. {
  547. rt_kprintf("Usage: vi [FILE]\n"); /*bb_show_usage();*/
  548. goto vi_exit;
  549. }
  550. // "Save cursor, use alternate screen buffer, clear screen"
  551. write1("\033[?1049h");
  552. // This is the main file handling loop
  553. while (1)
  554. {
  555. edit_file(file_name); // might be NULL on 1st iteration
  556. // NB: optind can be changed by ":next" and ":rewind" commands
  557. options.optind++;
  558. file_name = optparse_arg(&options);
  559. if (options.optind >= cmdline_filecnt)
  560. break;
  561. }
  562. // "Use normal screen buffer, restore cursor"
  563. write1("\033[?1049l");
  564. /* RT-Thread team added */
  565. vi_exit:
  566. vi_free(text);
  567. vi_free(screen);
  568. vi_free(current_filename);
  569. #if ENABLE_FEATURE_VI_DOT_CMD
  570. vi_free(ioq_start);
  571. #endif
  572. #if ENABLE_FEATURE_VI_SEARCH
  573. vi_free(last_search_pattern);
  574. #endif
  575. vi_free(ptr_to_globals);
  576. fflush_all();
  577. vi_mem_release();
  578. return 0;
  579. }
  580. MSH_CMD_EXPORT_ALIAS(vi_main, vi, a screen-oriented text editor);
  581. #if ENABLE_FEATURE_VI_COLON_EXPAND
  582. static void init_filename(char *fn)
  583. {
  584. char *copy = vi_strdup(fn);
  585. if (current_filename == NULL) {
  586. current_filename = copy;
  587. } else {
  588. vi_free(alt_filename);
  589. alt_filename = copy;
  590. }
  591. }
  592. #else
  593. # define init_filename(f) ((void)(0))
  594. #endif
  595. static void update_filename(char *fn)
  596. {
  597. #if ENABLE_FEATURE_VI_COLON_EXPAND
  598. if (fn == NULL)
  599. return;
  600. if (current_filename == NULL || strcmp(fn, current_filename) != 0) {
  601. vi_free(alt_filename);
  602. alt_filename = current_filename;
  603. current_filename = vi_strdup(fn);
  604. }
  605. #else
  606. if (fn != current_filename) {
  607. vi_free(current_filename);
  608. current_filename = vi_strdup(fn);
  609. }
  610. #endif
  611. }
  612. /* read text from file or create an empty buf */
  613. /* will also update current_filename */
  614. static int init_text_buffer(char *fn)
  615. {
  616. int rc;
  617. flush_undo_data();
  618. modified_count = 0;
  619. last_modified_count = -1;
  620. #if ENABLE_FEATURE_VI_YANKMARK
  621. /* init the marks */
  622. rt_memset(mark, 0, sizeof(mark));
  623. #endif
  624. /* allocate/reallocate text buffer */
  625. vi_free(text);
  626. text_size = 10240;
  627. screenbegin = dot = end = text = vi_zalloc(text_size);
  628. update_filename(fn);
  629. rc = file_insert(fn, text, 1);
  630. if (rc < 0) {
  631. // file doesnt exist. Start empty buf with dummy line
  632. char_insert(text, '\n', NO_UNDO);
  633. }
  634. return rc;
  635. }
  636. #if ENABLE_FEATURE_VI_WIN_RESIZE
  637. static int query_screen_dimensions(void)
  638. {
  639. int err = get_terminal_width_height(STDIN_FILENO, &columns, &rows);
  640. if (rows > MAX_SCR_ROWS)
  641. rows = MAX_SCR_ROWS;
  642. if (columns > MAX_SCR_COLS)
  643. columns = MAX_SCR_COLS;
  644. return err;
  645. }
  646. #else
  647. static int query_screen_dimensions(void) { return 0; }
  648. #endif
  649. static void edit_file(char *fn)
  650. {
  651. #if ENABLE_FEATURE_VI_YANKMARK
  652. #define cur_line edit_file__cur_line
  653. #endif
  654. int c;
  655. #if ENABLE_FEATURE_VI_USE_SIGNALS
  656. int sig;
  657. #endif
  658. editing = 1; // 0 = exit, 1 = one file, 2 = multiple files
  659. rawmode();
  660. rows = 24;
  661. columns = 80;
  662. IF_FEATURE_VI_ASK_TERMINAL(G.get_rowcol_error =) query_screen_dimensions();
  663. #if ENABLE_FEATURE_VI_ASK_TERMINAL
  664. if (G.get_rowcol_error /* TODO? && no input on stdin */) {
  665. uint64_t k;
  666. write1("\033[999;999H" "\033[6n");
  667. fflush_all();
  668. k = read_key(STDIN_FILENO, readbuffer, /*timeout_ms:*/ 100);
  669. if ((int32_t)k == KEYCODE_CURSOR_POS) {
  670. uint32_t rc = (k >> 32);
  671. columns = (rc & 0x7fff);
  672. if (columns > MAX_SCR_COLS)
  673. columns = MAX_SCR_COLS;
  674. rows = ((rc >> 16) & 0x7fff);
  675. if (rows > MAX_SCR_ROWS)
  676. rows = MAX_SCR_ROWS;
  677. }
  678. }
  679. #endif
  680. new_screen(rows, columns); // get memory for virtual screen
  681. init_text_buffer(fn);
  682. #if ENABLE_FEATURE_VI_YANKMARK
  683. YDreg = 26; // default Yank/Delete reg
  684. // Ureg = 27; - const // hold orig line for "U" cmd
  685. mark[26] = mark[27] = text; // init "previous context"
  686. #endif
  687. #if ENABLE_FEATURE_VI_CRASHME
  688. last_input_char = '\0';
  689. #endif
  690. crow = 0;
  691. ccol = 0;
  692. #if ENABLE_FEATURE_VI_USE_SIGNALS
  693. signal(SIGWINCH, winch_handler);
  694. signal(SIGTSTP, tstp_handler);
  695. sig = sigsetjmp(restart, 1);
  696. if (sig != 0) {
  697. screenbegin = dot = text;
  698. }
  699. // int_handler() can jump to "restart",
  700. // must install handler *after* initializing "restart"
  701. signal(SIGINT, int_handler);
  702. #endif
  703. cmd_mode = 0; // 0=command 1=insert 2='R'eplace
  704. cmdcnt = 0;
  705. offset = 0; // no horizontal offset
  706. c = '\0';
  707. #if ENABLE_FEATURE_VI_DOT_CMD
  708. vi_free(ioq_start);
  709. ioq_start = NULL;
  710. adding2q = 0;
  711. #endif
  712. #if ENABLE_FEATURE_VI_COLON
  713. {
  714. char *p, *q;
  715. int n = 0;
  716. while ((p = initial_cmds[n]) != NULL) {
  717. do {
  718. q = p;
  719. p = strchr(q, '\n');
  720. if (p)
  721. while (*p == '\n')
  722. *p++ = '\0';
  723. if (*q)
  724. colon(q);
  725. } while (p);
  726. vi_free(initial_cmds[n]);
  727. initial_cmds[n] = NULL;
  728. n++;
  729. }
  730. }
  731. #endif
  732. redraw(FALSE); // dont force every col re-draw
  733. //------This is the main Vi cmd handling loop -----------------------
  734. while (editing > 0) {
  735. #if ENABLE_FEATURE_VI_CRASHME
  736. if (crashme > 0) {
  737. if ((end - text) > 1) {
  738. crash_dummy(); // generate a random command
  739. } else {
  740. crashme = 0;
  741. string_insert(text, "\n\n##### Ran out of text to work on. #####\n\n", NO_UNDO); // insert the string
  742. dot = text;
  743. refresh(FALSE);
  744. }
  745. }
  746. #endif
  747. c = get_one_char(); // get a cmd from user
  748. #if ENABLE_FEATURE_VI_CRASHME
  749. last_input_char = c;
  750. #endif
  751. #if ENABLE_FEATURE_VI_YANKMARK
  752. // save a copy of the current line- for the 'U" command
  753. if (begin_line(dot) != cur_line) {
  754. cur_line = begin_line(dot);
  755. text_yank(begin_line(dot), end_line(dot), Ureg, PARTIAL);
  756. }
  757. #endif
  758. #if ENABLE_FEATURE_VI_DOT_CMD
  759. // If c is a command that changes text[],
  760. // (re)start remembering the input for the "." command.
  761. if (!adding2q
  762. && ioq_start == NULL
  763. && cmd_mode == 0 // command mode
  764. && c > '\0' // exclude NUL and non-ASCII chars
  765. && c < 0x7f // (Unicode and such)
  766. && strchr(modifying_cmds, c)
  767. ) {
  768. start_new_cmd_q(c);
  769. }
  770. #endif
  771. do_cmd(c); // execute the user command
  772. // poll to see if there is input already waiting. if we are
  773. // not able to display output fast enough to keep up, skip
  774. // the display update until we catch up with input.
  775. if (!readbuffer[0] && mysleep(0) == 0) {
  776. // no input pending - so update output
  777. refresh(FALSE);
  778. show_status_line();
  779. }
  780. #if ENABLE_FEATURE_VI_CRASHME
  781. if (crashme > 0)
  782. crash_test(); // test editor variables
  783. #endif
  784. }
  785. //-------------------------------------------------------------------
  786. go_bottom_and_clear_to_eol();
  787. cookmode();
  788. #undef cur_line
  789. }
  790. //----- The Colon commands -------------------------------------
  791. #if ENABLE_FEATURE_VI_COLON
  792. // Evaluate colon address expression. Returns a pointer to the
  793. // next character or NULL on error. If 'result' contains a valid
  794. // address 'valid' is TRUE.
  795. static char *get_one_address(char *p, int *result, int *valid)
  796. {
  797. int num, sign, addr, got_addr;
  798. # if ENABLE_FEATURE_VI_YANKMARK || ENABLE_FEATURE_VI_SEARCH
  799. char *q, c;
  800. # endif
  801. IF_FEATURE_VI_SEARCH(int dir;)
  802. got_addr = FALSE;
  803. addr = count_lines(text, dot); // default to current line
  804. sign = 0;
  805. for (;;) {
  806. if (isblank(*p)) {
  807. if (got_addr) {
  808. addr += sign;
  809. sign = 0;
  810. }
  811. p++;
  812. } else if (!got_addr && *p == '.') { // the current line
  813. p++;
  814. //addr = count_lines(text, dot);
  815. got_addr = TRUE;
  816. } else if (!got_addr && *p == '$') { // the last line in file
  817. p++;
  818. addr = count_lines(text, end - 1);
  819. got_addr = TRUE;
  820. }
  821. # if ENABLE_FEATURE_VI_YANKMARK
  822. else if (!got_addr && *p == '\'') { // is this a mark addr
  823. p++;
  824. c = tolower(*p);
  825. p++;
  826. q = NULL;
  827. if (c >= 'a' && c <= 'z') {
  828. // we have a mark
  829. c = c - 'a';
  830. q = mark[(unsigned char) c];
  831. }
  832. if (q == NULL) { // is mark valid
  833. status_line_bold("Mark not set");
  834. return NULL;
  835. }
  836. addr = count_lines(text, q);
  837. got_addr = TRUE;
  838. }
  839. # endif
  840. # if ENABLE_FEATURE_VI_SEARCH
  841. else if (!got_addr && (*p == '/' || *p == '?')) { // a search pattern
  842. c = *p;
  843. q = strchrnul(p + 1, c);
  844. if (p + 1 != q) {
  845. // save copy of new pattern
  846. vi_free(last_search_pattern);
  847. last_search_pattern = vi_strndup(p, q - p);
  848. }
  849. p = q;
  850. if (*p == c)
  851. p++;
  852. if (c == '/') {
  853. q = next_line(dot);
  854. dir = (FORWARD << 1) | FULL;
  855. } else {
  856. q = begin_line(dot);
  857. dir = ((unsigned)BACK << 1) | FULL;
  858. }
  859. q = char_search(q, last_search_pattern + 1, dir);
  860. if (q == NULL) {
  861. // no match, continue from other end of file
  862. q = char_search(dir > 0 ? text : end - 1,
  863. last_search_pattern + 1, dir);
  864. if (q == NULL) {
  865. status_line_bold("Pattern not found");
  866. return NULL;
  867. }
  868. }
  869. addr = count_lines(text, q);
  870. got_addr = TRUE;
  871. }
  872. # endif
  873. else if (isdigit(*p)) {
  874. num = 0;
  875. while (isdigit(*p))
  876. num = num * 10 + *p++ -'0';
  877. if (!got_addr) { // specific line number
  878. addr = num;
  879. got_addr = TRUE;
  880. } else { // offset from current addr
  881. addr += sign >= 0 ? num : -num;
  882. }
  883. sign = 0;
  884. } else if (*p == '-' || *p == '+') {
  885. if (!got_addr) { // default address is dot
  886. //addr = count_lines(text, dot);
  887. got_addr = TRUE;
  888. } else {
  889. addr += sign;
  890. }
  891. sign = *p++ == '-' ? -1 : 1;
  892. } else {
  893. addr += sign; // consume unused trailing sign
  894. break;
  895. }
  896. }
  897. *result = addr;
  898. *valid = got_addr;
  899. return p;
  900. }
  901. # define GET_ADDRESS 0
  902. # define GET_SEPARATOR 1
  903. // Read line addresses for a colon command. The user can enter as
  904. // many as they like but only the last two will be used.
  905. static char *get_address(char *p, int *b, int *e, unsigned int *got)
  906. {
  907. int state = GET_ADDRESS;
  908. int valid;
  909. int addr;
  910. char *save_dot = dot;
  911. //----- get the address' i.e., 1,3 'a,'b -----
  912. for (;;) {
  913. if (isblank(*p)) {
  914. p++;
  915. } else if (state == GET_ADDRESS && *p == '%') { // alias for 1,$
  916. p++;
  917. *b = 1;
  918. *e = count_lines(text, end-1);
  919. *got = 3;
  920. state = GET_SEPARATOR;
  921. } else if (state == GET_ADDRESS) {
  922. valid = FALSE;
  923. p = get_one_address(p, &addr, &valid);
  924. // Quit on error or if the address is invalid and isn't of
  925. // the form ',$' or '1,' (in which case it defaults to dot).
  926. if (p == NULL || !(valid || *p == ',' || *p == ';' || *got & 1))
  927. break;
  928. *b = *e;
  929. *e = addr;
  930. *got = (*got << 1) | 1;
  931. state = GET_SEPARATOR;
  932. } else if (state == GET_SEPARATOR && (*p == ',' || *p == ';')) {
  933. if (*p == ';')
  934. dot = find_line(*e);
  935. p++;
  936. state = GET_ADDRESS;
  937. } else {
  938. break;
  939. }
  940. }
  941. dot = save_dot;
  942. return p;
  943. }
  944. # if ENABLE_FEATURE_VI_SET && ENABLE_FEATURE_VI_SETOPTS
  945. static void setops(char *args, int flg_no)
  946. {
  947. char *eq;
  948. int index;
  949. eq = strchr(args, '=');
  950. if (eq) *eq = '\0';
  951. index = index_in_strings(OPTS_STR, args + flg_no);
  952. if (eq) *eq = '=';
  953. if (index < 0) {
  954. bad:
  955. status_line_bold("bad option: %s", args);
  956. return;
  957. }
  958. index = 1 << (index >> 1); // convert to VI_bit
  959. if (index & VI_TABSTOP) {
  960. int t;
  961. if (!eq || flg_no) // no "=NNN" or it is "notabstop"?
  962. goto bad;
  963. t = vi_strtou(eq + 1, NULL, 10);
  964. if (t <= 0 || t > MAX_TABSTOP)
  965. goto bad;
  966. tabstop = t;
  967. return;
  968. }
  969. if (eq) goto bad; // boolean option has "="?
  970. if (flg_no) {
  971. vi_setops &= ~index;
  972. } else {
  973. vi_setops |= index;
  974. }
  975. }
  976. # endif
  977. # if ENABLE_FEATURE_VI_COLON_EXPAND
  978. static char *expand_args(char *args)
  979. {
  980. char *s, *t;
  981. const char *replace;
  982. args = vi_strdup(args);
  983. for (s = args; *s; s++) {
  984. if (*s == '%') {
  985. replace = current_filename;
  986. } else if (*s == '#') {
  987. replace = alt_filename;
  988. } else {
  989. if (*s == '\\' && s[1] != '\0') {
  990. for (t = s++; *t; t++)
  991. *t = t[1];
  992. }
  993. continue;
  994. }
  995. if (replace == NULL) {
  996. vi_free(args);
  997. status_line_bold("No previous filename");
  998. return NULL;
  999. }
  1000. *s = '\0';
  1001. t = xasprintf("%s%s%s", args, replace, s+1);
  1002. s = t + (s - args) + strlen(replace);
  1003. vi_free(args);
  1004. args = t;
  1005. }
  1006. return args;
  1007. }
  1008. # else
  1009. # define expand_args(a) (a)
  1010. # endif
  1011. #endif /* FEATURE_VI_COLON */
  1012. #if ENABLE_FEATURE_VI_REGEX_SEARCH
  1013. # define MAX_SUBPATTERN 10 // subpatterns \0 .. \9
  1014. // Like strchr() but skipping backslash-escaped characters
  1015. static char *strchr_backslash(const char *s, int c)
  1016. {
  1017. while (*s) {
  1018. if (*s == c)
  1019. return (char *)s;
  1020. if (*s == '\\')
  1021. if (*++s == '\0')
  1022. break;
  1023. s++;
  1024. }
  1025. return NULL;
  1026. }
  1027. // If the return value is not NULL the caller should free R
  1028. static char *regex_search(char *q, regex_t *preg, const char *Rorig,
  1029. size_t *len_F, size_t *len_R, char **R)
  1030. {
  1031. regmatch_t regmatch[MAX_SUBPATTERN], *cur_match;
  1032. char *found = NULL;
  1033. const char *t;
  1034. char *r;
  1035. regmatch[0].rm_so = 0;
  1036. regmatch[0].rm_eo = end_line(q) - q;
  1037. if (regexec(preg, q, MAX_SUBPATTERN, regmatch, REG_STARTEND) != 0)
  1038. return found;
  1039. found = q + regmatch[0].rm_so;
  1040. *len_F = regmatch[0].rm_eo - regmatch[0].rm_so;
  1041. *R = NULL;
  1042. fill_result:
  1043. // first pass calculates len_R, second fills R
  1044. *len_R = 0;
  1045. for (t = Rorig, r = *R; *t; t++) {
  1046. size_t len = 1; // default is to copy one char from replace pattern
  1047. const char *from = t;
  1048. if (*t == '\\') {
  1049. from = ++t; // skip backslash
  1050. if (*t >= '0' && *t < '0' + MAX_SUBPATTERN) {
  1051. cur_match = regmatch + (*t - '0');
  1052. if (cur_match->rm_so >= 0) {
  1053. len = cur_match->rm_eo - cur_match->rm_so;
  1054. from = q + cur_match->rm_so;
  1055. }
  1056. }
  1057. }
  1058. *len_R += len;
  1059. if (*R) {
  1060. rt_memcpy(r, from, len);
  1061. r += len;
  1062. /* *r = '\0'; - xzalloc did it */
  1063. }
  1064. }
  1065. if (*R == NULL) {
  1066. *R = vi_zalloc(*len_R + 1);
  1067. goto fill_result;
  1068. }
  1069. return found;
  1070. }
  1071. #else /* !ENABLE_FEATURE_VI_REGEX_SEARCH */
  1072. # define strchr_backslash(s, c) strchr(s, c)
  1073. #endif /* ENABLE_FEATURE_VI_REGEX_SEARCH */
  1074. // buf must be no longer than MAX_INPUT_LEN!
  1075. static void colon(char *buf)
  1076. {
  1077. #if !ENABLE_FEATURE_VI_COLON
  1078. /* Simple ":cmd" handler with minimal set of commands */
  1079. char *p = buf;
  1080. int cnt;
  1081. if (*p == ':')
  1082. p++;
  1083. cnt = strlen(p);
  1084. if (cnt == 0)
  1085. return;
  1086. if (strncmp(p, "quit", cnt) == 0
  1087. || strcmp(p, "q!") == 0
  1088. ) {
  1089. if (modified_count && p[1] != '!') {
  1090. status_line_bold("No write since last change (:%s! overrides)", p);
  1091. } else {
  1092. editing = 0;
  1093. }
  1094. return;
  1095. }
  1096. if (strncmp(p, "write", cnt) == 0
  1097. || strcmp(p, "wq") == 0
  1098. || strcmp(p, "wn") == 0
  1099. || (p[0] == 'x' && !p[1])
  1100. ) {
  1101. cnt = file_write(current_filename, text, end - 1);
  1102. if (cnt < 0) {
  1103. if (cnt == -1)
  1104. status_line_bold("Write error: %s", strerror(errno));
  1105. } else {
  1106. modified_count = 0;
  1107. last_modified_count = -1;
  1108. status_line("'%s' %uL, %uC",
  1109. current_filename,
  1110. count_lines(text, end - 1), cnt
  1111. );
  1112. if (p[0] == 'x' || p[1] == 'q' || p[1] == 'n'
  1113. || p[0] == 'X' || p[1] == 'Q' || p[1] == 'N'
  1114. ) {
  1115. editing = 0;
  1116. }
  1117. }
  1118. return;
  1119. }
  1120. if (strncmp(p, "file", cnt) == 0) {
  1121. last_status_cksum = 0; // force status update
  1122. return;
  1123. }
  1124. if (sscanf(p, "%d", &cnt) > 0) {
  1125. dot = find_line(cnt);
  1126. dot_skip_over_ws();
  1127. return;
  1128. }
  1129. not_implemented(p);
  1130. #else
  1131. // check how many addresses we got
  1132. # define GOT_ADDRESS (got & 1)
  1133. # define GOT_RANGE ((got & 3) == 3)
  1134. char c, *buf1, *q, *r;
  1135. char *fn, cmd[MAX_INPUT_LEN], *cmdend, *args, *exp = NULL;
  1136. int i, l, li, b, e;
  1137. unsigned int got;
  1138. int useforce;
  1139. // :3154 // if (-e line 3154) goto it else stay put
  1140. // :4,33w! foo // write a portion of buffer to file "foo"
  1141. // :w // write all of buffer to current file
  1142. // :q // quit
  1143. // :q! // quit- dont care about modified file
  1144. // :'a,'z!sort -u // filter block through sort
  1145. // :'f // goto mark "f"
  1146. // :'fl // list literal the mark "f" line
  1147. // :.r bar // read file "bar" into buffer before dot
  1148. // :/123/,/abc/d // delete lines from "123" line to "abc" line
  1149. // :/xyz/ // goto the "xyz" line
  1150. // :s/find/replace/ // substitute pattern "find" with "replace"
  1151. // :!<cmd> // run <cmd> then return
  1152. //
  1153. while (*buf == ':')
  1154. buf++; // move past leading colons
  1155. while (isblank(*buf))
  1156. buf++; // move past leading blanks
  1157. if (!buf[0] || buf[0] == '"')
  1158. goto ret; // ignore empty lines or those starting with '"'
  1159. li = i = 0;
  1160. b = e = -1;
  1161. got = 0;
  1162. li = count_lines(text, end - 1);
  1163. fn = current_filename;
  1164. // look for optional address(es) :. :1 :1,9 :'q,'a :%
  1165. buf = get_address(buf, &b, &e, &got);
  1166. if (buf == NULL) {
  1167. goto ret;
  1168. }
  1169. // get the COMMAND into cmd[]
  1170. strcpy(cmd, buf);
  1171. buf1 = cmd;
  1172. while (!isspace(*buf1) && *buf1 != '\0') {
  1173. buf1++;
  1174. }
  1175. cmdend = buf1;
  1176. // get any ARGuments
  1177. while (isblank(*buf1))
  1178. buf1++;
  1179. args = buf1;
  1180. *cmdend = '\0';
  1181. useforce = FALSE;
  1182. if (cmdend > cmd && cmdend[-1] == '!') {
  1183. useforce = TRUE;
  1184. cmdend[-1] = '\0'; // get rid of !
  1185. }
  1186. // assume the command will want a range, certain commands
  1187. // (read, substitute) need to adjust these assumptions
  1188. if (!GOT_ADDRESS) {
  1189. q = text; // no addr, use 1,$ for the range
  1190. r = end - 1;
  1191. } else {
  1192. // at least one addr was given, get its details
  1193. if (e < 0 || e > li) {
  1194. status_line_bold("Invalid range");
  1195. goto ret;
  1196. }
  1197. q = r = find_line(e);
  1198. if (!GOT_RANGE) {
  1199. // if there is only one addr, then it's the line
  1200. // number of the single line the user wants.
  1201. // Reset the end pointer to the end of that line.
  1202. r = end_line(q);
  1203. li = 1;
  1204. } else {
  1205. // we were given two addrs. change the
  1206. // start pointer to the addr given by user.
  1207. if (b < 0 || b > li || b > e) {
  1208. status_line_bold("Invalid range");
  1209. goto ret;
  1210. }
  1211. q = find_line(b); // what line is #b
  1212. r = end_line(r);
  1213. li = e - b + 1;
  1214. }
  1215. }
  1216. // ------------ now look for the command ------------
  1217. i = strlen(cmd);
  1218. if (i == 0) { // :123CR goto line #123
  1219. if (e >= 0) {
  1220. dot = find_line(e); // what line is #e
  1221. dot_skip_over_ws();
  1222. }
  1223. }
  1224. #if ENABLE_FEATURE_ALLOW_EXEC
  1225. else if (cmd[0] == '!') { // run a cmd
  1226. int retcode;
  1227. // :!ls run the <cmd>
  1228. exp = expand_args(buf + 1);
  1229. if (exp == NULL)
  1230. goto ret;
  1231. go_bottom_and_clear_to_eol();
  1232. cookmode();
  1233. retcode = system(exp); // run the cmd
  1234. if (retcode)
  1235. rt_kprintf("\nshell returned %i\n\n", retcode);
  1236. rawmode();
  1237. Hit_Return(); // let user see results
  1238. }
  1239. #endif
  1240. else if (cmd[0] == '=' && !cmd[1]) { // where is the address
  1241. if (!GOT_ADDRESS) { // no addr given- use defaults
  1242. e = count_lines(text, dot);
  1243. }
  1244. status_line("%d", e);
  1245. } else if (strncmp(cmd, "delete", i) == 0) { // delete lines
  1246. if (!GOT_ADDRESS) { // no addr given- use defaults
  1247. q = begin_line(dot); // assume .,. for the range
  1248. r = end_line(dot);
  1249. }
  1250. dot = yank_delete(q, r, WHOLE, YANKDEL, ALLOW_UNDO); // save, then delete lines
  1251. dot_skip_over_ws();
  1252. } else if (strncmp(cmd, "edit", i) == 0) { // Edit a file
  1253. int size;
  1254. // don't edit, if the current file has been modified
  1255. if (modified_count && !useforce) {
  1256. status_line_bold("No write since last change (:%s! overrides)", cmd);
  1257. goto ret;
  1258. }
  1259. if (args[0]) {
  1260. // the user supplied a file name
  1261. fn = exp = expand_args(args);
  1262. if (exp == NULL)
  1263. goto ret;
  1264. } else if (current_filename == NULL) {
  1265. // no user file name, no current name- punt
  1266. status_line_bold("No current filename");
  1267. goto ret;
  1268. }
  1269. size = init_text_buffer(fn);
  1270. #if ENABLE_FEATURE_VI_YANKMARK
  1271. if (Ureg >= 0 && Ureg < 28) {
  1272. vi_free(reg[Ureg]); // free orig line reg- for 'U'
  1273. reg[Ureg] = NULL;
  1274. }
  1275. /*if (YDreg < 28) - always true*/ {
  1276. vi_free(reg[YDreg]); // free default yank/delete register
  1277. reg[YDreg] = NULL;
  1278. }
  1279. #endif
  1280. // how many lines in text[]?
  1281. li = count_lines(text, end - 1);
  1282. status_line("'%s'%s"
  1283. IF_FEATURE_VI_READONLY("%s")
  1284. " %uL, %uC",
  1285. fn,
  1286. (size < 0 ? " [New file]" : ""),
  1287. IF_FEATURE_VI_READONLY(
  1288. ((readonly_mode) ? " [Readonly]" : ""),
  1289. )
  1290. li, (int)(end - text)
  1291. );
  1292. } else if (strncmp(cmd, "file", i) == 0) { // what File is this
  1293. if (e >= 0) {
  1294. status_line_bold("No address allowed on this command");
  1295. goto ret;
  1296. }
  1297. if (args[0]) {
  1298. // user wants a new filename
  1299. exp = expand_args(args);
  1300. if (exp == NULL)
  1301. goto ret;
  1302. update_filename(exp);
  1303. } else {
  1304. // user wants file status info
  1305. last_status_cksum = 0; // force status update
  1306. }
  1307. } else if (strncmp(cmd, "features", i) == 0) { // what features are available
  1308. // print out values of all features
  1309. go_bottom_and_clear_to_eol();
  1310. cookmode();
  1311. show_help();
  1312. rawmode();
  1313. Hit_Return();
  1314. } else if (strncmp(cmd, "list", i) == 0) { // literal print line
  1315. if (!GOT_ADDRESS) { // no addr given- use defaults
  1316. q = begin_line(dot); // assume .,. for the range
  1317. r = end_line(dot);
  1318. }
  1319. go_bottom_and_clear_to_eol();
  1320. rt_kprintf("\r");
  1321. for (; q <= r; q++) {
  1322. int c_is_no_print;
  1323. c = *q;
  1324. c_is_no_print = (c & 0x80) && !Isprint(c);
  1325. if (c_is_no_print) {
  1326. c = '.';
  1327. standout_start();
  1328. }
  1329. if (c == '\n') {
  1330. write1("$\r");
  1331. } else if (c < ' ' || c == 127) {
  1332. vi_putchar('^');
  1333. if (c == 127)
  1334. c = '?';
  1335. else
  1336. c += '@';
  1337. }
  1338. vi_putchar(c);
  1339. if (c_is_no_print)
  1340. standout_end();
  1341. }
  1342. Hit_Return();
  1343. } else if (strncmp(cmd, "quit", i) == 0 // quit
  1344. || strncmp(cmd, "next", i) == 0 // edit next file
  1345. || strncmp(cmd, "prev", i) == 0 // edit previous file
  1346. ) {
  1347. int n;
  1348. if (useforce) {
  1349. if (*cmd == 'q') {
  1350. // force end of argv list
  1351. options.optind = cmdline_filecnt;
  1352. }
  1353. editing = 0;
  1354. goto ret;
  1355. }
  1356. // don't exit if the file been modified
  1357. if (modified_count) {
  1358. status_line_bold("No write since last change (:%s! overrides)", cmd);
  1359. goto ret;
  1360. }
  1361. // are there other file to edit
  1362. n = cmdline_filecnt - options.optind - 1;
  1363. if (*cmd == 'q' && n > 0) {
  1364. status_line_bold("%u more file(s) to edit", n);
  1365. goto ret;
  1366. }
  1367. if (*cmd == 'n' && n <= 0) {
  1368. status_line_bold("No more files to edit");
  1369. goto ret;
  1370. }
  1371. if (*cmd == 'p') {
  1372. // are there previous files to edit
  1373. if (options.optind < 1) {
  1374. status_line_bold("No previous files to edit");
  1375. goto ret;
  1376. }
  1377. options.optind -= 2;
  1378. }
  1379. editing = 0;
  1380. } else if (strncmp(cmd, "read", i) == 0) { // read file into text[]
  1381. int size, num;
  1382. if (args[0]) {
  1383. // the user supplied a file name
  1384. fn = exp = expand_args(args);
  1385. if (exp == NULL)
  1386. goto ret;
  1387. init_filename(fn);
  1388. } else if (current_filename == NULL) {
  1389. // no user file name, no current name- punt
  1390. status_line_bold("No current filename");
  1391. goto ret;
  1392. }
  1393. if (e == 0) { // user said ":0r foo"
  1394. q = text;
  1395. } else { // read after given line or current line if none given
  1396. q = next_line(GOT_ADDRESS ? find_line(e) : dot);
  1397. // read after last line
  1398. if (q == end-1)
  1399. ++q;
  1400. }
  1401. num = count_lines(text, q);
  1402. if (q == end)
  1403. num++;
  1404. { // dance around potentially-reallocated text[]
  1405. uintptr_t ofs = q - text;
  1406. size = file_insert(fn, q, 0);
  1407. q = text + ofs;
  1408. }
  1409. if (size < 0)
  1410. goto ret; // nothing was inserted
  1411. // how many lines in text[]?
  1412. li = count_lines(q, q + size - 1);
  1413. status_line("'%s'"
  1414. IF_FEATURE_VI_READONLY("%s")
  1415. " %uL, %uC",
  1416. fn,
  1417. IF_FEATURE_VI_READONLY((readonly_mode ? " [Readonly]" : ""),)
  1418. li, size
  1419. );
  1420. dot = find_line(num);
  1421. } else if (strncmp(cmd, "rewind", i) == 0) { // rewind cmd line args
  1422. if (modified_count && !useforce) {
  1423. status_line_bold("No write since last change (:%s! overrides)", cmd);
  1424. } else {
  1425. // reset the filenames to edit
  1426. options.optind = -1; /* start from 0th file */
  1427. editing = 0;
  1428. }
  1429. #if ENABLE_FEATURE_VI_SET
  1430. } else if (strncmp(cmd, "set", i) == 0) { // set or clear features
  1431. #if ENABLE_FEATURE_VI_SETOPTS
  1432. char *argp, *argn, oldch;
  1433. #endif
  1434. // only blank is regarded as args delimiter. What about tab '\t'?
  1435. if (!args[0] || strcmp(args, "all") == 0) {
  1436. // print out values of all options
  1437. #if ENABLE_FEATURE_VI_SETOPTS
  1438. status_line_bold(
  1439. "%sautoindent "
  1440. "%sexpandtab "
  1441. "%sflash "
  1442. "%signorecase "
  1443. "%sshowmatch "
  1444. "tabstop=%u",
  1445. autoindent ? "" : "no",
  1446. expandtab ? "" : "no",
  1447. err_method ? "" : "no",
  1448. ignorecase ? "" : "no",
  1449. showmatch ? "" : "no",
  1450. tabstop
  1451. );
  1452. #endif
  1453. goto ret;
  1454. }
  1455. #if ENABLE_FEATURE_VI_SETOPTS
  1456. argp = args;
  1457. while (*argp) {
  1458. i = 0;
  1459. if (argp[0] == 'n' && argp[1] == 'o') // "noXXX"
  1460. i = 2;
  1461. argn = skip_non_whitespace(argp);
  1462. oldch = *argn;
  1463. *argn = '\0';
  1464. setops(argp, i);
  1465. *argn = oldch;
  1466. argp = skip_whitespace(argn);
  1467. }
  1468. #endif /* FEATURE_VI_SETOPTS */
  1469. #endif /* FEATURE_VI_SET */
  1470. #if ENABLE_FEATURE_VI_SEARCH
  1471. } else if (cmd[0] == 's') { // substitute a pattern with a replacement pattern
  1472. char *F, *R, *flags;
  1473. size_t len_F, len_R;
  1474. int gflag = 0; // global replace flag
  1475. int subs = 0; // number of substitutions
  1476. # if ENABLE_FEATURE_VI_VERBOSE_STATUS
  1477. int last_line = 0, lines = 0;
  1478. # endif
  1479. # if ENABLE_FEATURE_VI_REGEX_SEARCH
  1480. regex_t preg;
  1481. int cflags;
  1482. char *Rorig;
  1483. # if ENABLE_FEATURE_VI_UNDO
  1484. int undo = 0;
  1485. # endif
  1486. # endif
  1487. // F points to the "find" pattern
  1488. // R points to the "replace" pattern
  1489. // replace the cmd line delimiters "/" with NULs
  1490. c = buf[1]; // what is the delimiter
  1491. F = buf + 2; // start of "find"
  1492. R = strchr_backslash(F, c); // middle delimiter
  1493. if (!R)
  1494. goto colon_s_fail;
  1495. len_F = R - F;
  1496. *R++ = '\0'; // terminate "find"
  1497. flags = strchr_backslash(R, c);
  1498. if (flags) {
  1499. *flags++ = '\0'; // terminate "replace"
  1500. gflag = *flags;
  1501. }
  1502. if (len_F) { // save "find" as last search pattern
  1503. vi_free(last_search_pattern);
  1504. last_search_pattern = vi_strdup(F - 1);
  1505. last_search_pattern[0] = '/';
  1506. } else if (last_search_pattern[1] == '\0') {
  1507. status_line_bold("No previous search");
  1508. goto ret;
  1509. } else {
  1510. F = last_search_pattern + 1;
  1511. len_F = strlen(F);
  1512. }
  1513. if (!GOT_ADDRESS) { // no addr given
  1514. q = begin_line(dot); // start with cur line
  1515. r = end_line(dot);
  1516. b = e = count_lines(text, q); // cur line number
  1517. } else if (!GOT_RANGE) { // one addr given
  1518. b = e;
  1519. }
  1520. # if ENABLE_FEATURE_VI_REGEX_SEARCH
  1521. Rorig = R;
  1522. cflags = 0;
  1523. if (ignorecase)
  1524. cflags = REG_ICASE;
  1525. rt_memset(&preg, 0, sizeof(preg));
  1526. if (regcomp(&preg, F, cflags) != 0) {
  1527. status_line(":s bad search pattern");
  1528. goto regex_search_end;
  1529. }
  1530. # else
  1531. len_R = strlen(R);
  1532. # endif
  1533. for (i = b; i <= e; i++) { // so, :20,23 s \0 find \0 replace \0
  1534. char *ls = q; // orig line start
  1535. char *found;
  1536. vc4:
  1537. # if ENABLE_FEATURE_VI_REGEX_SEARCH
  1538. found = regex_search(q, &preg, Rorig, &len_F, &len_R, &R);
  1539. # else
  1540. found = char_search(q, F, (FORWARD << 1) | LIMITED); // search cur line only for "find"
  1541. # endif
  1542. if (found) {
  1543. uintptr_t bias;
  1544. // we found the "find" pattern - delete it
  1545. // For undo support, the first item should not be chained
  1546. // This needs to be handled differently depending on
  1547. // whether or not regex support is enabled.
  1548. # if ENABLE_FEATURE_VI_REGEX_SEARCH
  1549. # define TEST_LEN_F len_F // len_F may be zero
  1550. # define TEST_UNDO1 undo++
  1551. # define TEST_UNDO2 undo++
  1552. # else
  1553. # define TEST_LEN_F 1 // len_F is never zero
  1554. # define TEST_UNDO1 subs
  1555. # define TEST_UNDO2 1
  1556. # endif
  1557. if (TEST_LEN_F) // match can be empty, no delete needed
  1558. text_hole_delete(found, found + len_F - 1,
  1559. TEST_UNDO1 ? ALLOW_UNDO_CHAIN : ALLOW_UNDO);
  1560. if (len_R != 0) { // insert the "replace" pattern, if required
  1561. bias = string_insert(found, R,
  1562. TEST_UNDO2 ? ALLOW_UNDO_CHAIN : ALLOW_UNDO);
  1563. found += bias;
  1564. ls += bias;
  1565. //q += bias; - recalculated anyway
  1566. }
  1567. # if ENABLE_FEATURE_VI_REGEX_SEARCH
  1568. vi_free(R);
  1569. # endif
  1570. if (TEST_LEN_F || len_R != 0) {
  1571. dot = ls;
  1572. subs++;
  1573. # if ENABLE_FEATURE_VI_VERBOSE_STATUS
  1574. if (last_line != i) {
  1575. last_line = i;
  1576. ++lines;
  1577. }
  1578. # endif
  1579. }
  1580. // check for "global" :s/foo/bar/g
  1581. if (gflag == 'g') {
  1582. if ((found + len_R) < end_line(ls)) {
  1583. q = found + len_R;
  1584. goto vc4; // don't let q move past cur line
  1585. }
  1586. }
  1587. }
  1588. q = next_line(ls);
  1589. }
  1590. if (subs == 0) {
  1591. status_line_bold("No match");
  1592. } else {
  1593. dot_skip_over_ws();
  1594. # if ENABLE_FEATURE_VI_VERBOSE_STATUS
  1595. if (subs > 1)
  1596. status_line("%d substitutions on %d lines", subs, lines);
  1597. # endif
  1598. }
  1599. # if ENABLE_FEATURE_VI_REGEX_SEARCH
  1600. regex_search_end:
  1601. regfree(&preg);
  1602. # endif
  1603. #endif /* FEATURE_VI_SEARCH */
  1604. } else if (strncmp(cmd, "version", i) == 0) { // show software version
  1605. status_line(BB_VER " " BB_BT);
  1606. } else if (strncmp(cmd, "write", i) == 0 // write text to file
  1607. || strcmp(cmd, "wq") == 0
  1608. || strcmp(cmd, "wn") == 0
  1609. || (cmd[0] == 'x' && !cmd[1])
  1610. ) {
  1611. int size;
  1612. //int forced = FALSE;
  1613. // is there a file name to write to?
  1614. if (args[0]) {
  1615. struct stat statbuf;
  1616. exp = expand_args(args);
  1617. if (exp == NULL)
  1618. goto ret;
  1619. if (!useforce && (fn == NULL || strcmp(fn, exp) != 0) &&
  1620. stat(exp, &statbuf) == 0) {
  1621. status_line_bold("File exists (:w! overrides)");
  1622. goto ret;
  1623. }
  1624. fn = exp;
  1625. init_filename(fn);
  1626. }
  1627. # if ENABLE_FEATURE_VI_READONLY
  1628. else if (readonly_mode && !useforce && fn) {
  1629. status_line_bold("'%s' is read only", fn);
  1630. goto ret;
  1631. }
  1632. # endif
  1633. //if (useforce) {
  1634. // if "fn" is not write-able, chmod u+w
  1635. // sprintf(syscmd, "chmod u+w %s", fn);
  1636. // system(syscmd);
  1637. // forced = TRUE;
  1638. //}
  1639. if (modified_count != 0 || cmd[0] != 'x') {
  1640. size = r - q + 1;
  1641. l = file_write(fn, q, r);
  1642. } else {
  1643. size = 0;
  1644. l = 0;
  1645. }
  1646. //if (useforce && forced) {
  1647. // chmod u-w
  1648. // sprintf(syscmd, "chmod u-w %s", fn);
  1649. // system(syscmd);
  1650. // forced = FALSE;
  1651. //}
  1652. if (l < 0) {
  1653. if (l == -1)
  1654. status_line_bold_errno(fn);
  1655. } else {
  1656. // how many lines written
  1657. li = count_lines(q, q + l - 1);
  1658. status_line("'%s' %uL, %uC", fn, li, l);
  1659. if (l == size) {
  1660. if (q == text && q + l == end) {
  1661. modified_count = 0;
  1662. last_modified_count = -1;
  1663. }
  1664. if (cmd[1] == 'n') {
  1665. editing = 0;
  1666. } else if (cmd[0] == 'x' || cmd[1] == 'q') {
  1667. // are there other files to edit?
  1668. int n = cmdline_filecnt - options.optind - 1;
  1669. if (n > 0) {
  1670. if (useforce) {
  1671. // force end of argv list
  1672. options.optind = cmdline_filecnt;
  1673. } else {
  1674. status_line_bold("%u more file(s) to edit", n);
  1675. goto ret;
  1676. }
  1677. }
  1678. editing = 0;
  1679. }
  1680. }
  1681. }
  1682. # if ENABLE_FEATURE_VI_YANKMARK
  1683. } else if (strncmp(cmd, "yank", i) == 0) { // yank lines
  1684. if (!GOT_ADDRESS) { // no addr given- use defaults
  1685. q = begin_line(dot); // assume .,. for the range
  1686. r = end_line(dot);
  1687. }
  1688. text_yank(q, r, YDreg, WHOLE);
  1689. li = count_lines(q, r);
  1690. status_line("Yank %d lines (%d chars) into [%c]",
  1691. li, strlen(reg[YDreg]), what_reg());
  1692. # endif
  1693. } else {
  1694. // cmd unknown
  1695. not_implemented(cmd);
  1696. }
  1697. ret:
  1698. # if ENABLE_FEATURE_VI_COLON_EXPAND
  1699. vi_free(exp);
  1700. # endif
  1701. dot = bound_dot(dot); // make sure "dot" is valid
  1702. return;
  1703. # if ENABLE_FEATURE_VI_SEARCH
  1704. colon_s_fail:
  1705. status_line(":s expression missing delimiters");
  1706. # endif
  1707. #endif /* FEATURE_VI_COLON */
  1708. }
  1709. static void Hit_Return(void)
  1710. {
  1711. int c;
  1712. standout_start();
  1713. write1("[Hit return to continue]");
  1714. standout_end();
  1715. while ((c = get_one_char()) != '\n' && c != '\r')
  1716. continue;
  1717. redraw(TRUE); // force redraw all
  1718. }
  1719. static int next_tabstop(int col)
  1720. {
  1721. return col + ((tabstop - 1) - (col % tabstop));
  1722. }
  1723. static int prev_tabstop(int col)
  1724. {
  1725. return col - ((col % tabstop) ? (col % tabstop) : tabstop);
  1726. }
  1727. static int next_column(char c, int co)
  1728. {
  1729. if (c == '\t')
  1730. co = next_tabstop(co);
  1731. else if ((unsigned char)c < ' ' || c == 0x7f)
  1732. co++; // display as ^X, use 2 columns
  1733. return co + 1;
  1734. }
  1735. static int get_column(char *p)
  1736. {
  1737. const char *r;
  1738. int co = 0;
  1739. for (r = begin_line(p); r < p; r++)
  1740. co = next_column(*r, co);
  1741. return co;
  1742. }
  1743. //----- Synchronize the cursor to Dot --------------------------
  1744. static void sync_cursor(char *d, int *row, int *col)
  1745. {
  1746. char *beg_cur; // begin and end of "d" line
  1747. char *tp;
  1748. int cnt, ro, co;
  1749. beg_cur = begin_line(d); // first char of cur line
  1750. if (beg_cur < screenbegin) {
  1751. // "d" is before top line on screen
  1752. // how many lines do we have to move
  1753. cnt = count_lines(beg_cur, screenbegin);
  1754. sc1:
  1755. screenbegin = beg_cur;
  1756. if (cnt > (rows - 1) / 2) {
  1757. // we moved too many lines. put "dot" in middle of screen
  1758. for (cnt = 0; cnt < (rows - 1) / 2; cnt++) {
  1759. screenbegin = prev_line(screenbegin);
  1760. }
  1761. }
  1762. } else {
  1763. char *end_scr; // begin and end of screen
  1764. end_scr = end_screen(); // last char of screen
  1765. if (beg_cur > end_scr) {
  1766. // "d" is after bottom line on screen
  1767. // how many lines do we have to move
  1768. cnt = count_lines(end_scr, beg_cur);
  1769. if (cnt > (rows - 1) / 2)
  1770. goto sc1; // too many lines
  1771. for (ro = 0; ro < cnt - 1; ro++) {
  1772. // move screen begin the same amount
  1773. screenbegin = next_line(screenbegin);
  1774. // now, move the end of screen
  1775. end_scr = next_line(end_scr);
  1776. end_scr = end_line(end_scr);
  1777. }
  1778. }
  1779. }
  1780. // "d" is on screen- find out which row
  1781. tp = screenbegin;
  1782. for (ro = 0; ro < rows - 1; ro++) { // drive "ro" to correct row
  1783. if (tp == beg_cur)
  1784. break;
  1785. tp = next_line(tp);
  1786. }
  1787. // find out what col "d" is on
  1788. co = 0;
  1789. do { // drive "co" to correct column
  1790. if (*tp == '\n') //vda || *tp == '\0')
  1791. break;
  1792. co = next_column(*tp, co) - 1;
  1793. // inserting text before a tab, don't include its position
  1794. if (cmd_mode && tp == d - 1 && *d == '\t') {
  1795. co++;
  1796. break;
  1797. }
  1798. } while (tp++ < d && ++co);
  1799. // "co" is the column where "dot" is.
  1800. // The screen has "columns" columns.
  1801. // The currently displayed columns are 0+offset -- columns+ofset
  1802. // |-------------------------------------------------------------|
  1803. // ^ ^ ^
  1804. // offset | |------- columns ----------------|
  1805. //
  1806. // If "co" is already in this range then we do not have to adjust offset
  1807. // but, we do have to subtract the "offset" bias from "co".
  1808. // If "co" is outside this range then we have to change "offset".
  1809. // If the first char of a line is a tab the cursor will try to stay
  1810. // in column 7, but we have to set offset to 0.
  1811. if (co < 0 + offset) {
  1812. offset = co;
  1813. }
  1814. if (co >= columns + offset) {
  1815. offset = co - columns + 1;
  1816. }
  1817. // if the first char of the line is a tab, and "dot" is sitting on it
  1818. // force offset to 0.
  1819. if (d == beg_cur && *d == '\t') {
  1820. offset = 0;
  1821. }
  1822. co -= offset;
  1823. *row = ro;
  1824. *col = co;
  1825. }
  1826. //----- Text Movement Routines ---------------------------------
  1827. static char *begin_line(char *p) // return pointer to first char cur line
  1828. {
  1829. if (p > text) {
  1830. p = memrchr(text, '\n', p - text);
  1831. if (!p)
  1832. return text;
  1833. return p + 1;
  1834. }
  1835. return p;
  1836. }
  1837. static char *end_line(char *p) // return pointer to NL of cur line
  1838. {
  1839. if (p < end - 1) {
  1840. p = memchr(p, '\n', end - p - 1);
  1841. if (!p)
  1842. return end - 1;
  1843. }
  1844. return p;
  1845. }
  1846. static char *dollar_line(char *p) // return pointer to just before NL line
  1847. {
  1848. p = end_line(p);
  1849. // Try to stay off of the Newline
  1850. if (*p == '\n' && (p - begin_line(p)) > 0)
  1851. p--;
  1852. return p;
  1853. }
  1854. static char *prev_line(char *p) // return pointer first char prev line
  1855. {
  1856. p = begin_line(p); // goto begining of cur line
  1857. if (p > text && p[-1] == '\n')
  1858. p--; // step to prev line
  1859. p = begin_line(p); // goto begining of prev line
  1860. return p;
  1861. }
  1862. static char *next_line(char *p) // return pointer first char next line
  1863. {
  1864. p = end_line(p);
  1865. if (p < end - 1 && *p == '\n')
  1866. p++; // step to next line
  1867. return p;
  1868. }
  1869. //----- Text Information Routines ------------------------------
  1870. static char *end_screen(void)
  1871. {
  1872. char *q;
  1873. int cnt;
  1874. // find new bottom line
  1875. q = screenbegin;
  1876. for (cnt = 0; cnt < rows - 2; cnt++)
  1877. q = next_line(q);
  1878. q = end_line(q);
  1879. return q;
  1880. }
  1881. // count line from start to stop
  1882. static int count_lines(char *start, char *stop)
  1883. {
  1884. char *q;
  1885. int cnt;
  1886. if (stop < start) { // start and stop are backwards- reverse them
  1887. q = start;
  1888. start = stop;
  1889. stop = q;
  1890. }
  1891. cnt = 0;
  1892. stop = end_line(stop);
  1893. while (start <= stop && start <= end - 1) {
  1894. start = end_line(start);
  1895. if (*start == '\n')
  1896. cnt++;
  1897. start++;
  1898. }
  1899. return cnt;
  1900. }
  1901. static char *find_line(int li) // find begining of line #li
  1902. {
  1903. char *q;
  1904. for (q = text; li > 1; li--) {
  1905. q = next_line(q);
  1906. }
  1907. return q;
  1908. }
  1909. //----- Dot Movement Routines ----------------------------------
  1910. static void dot_left(void)
  1911. {
  1912. undo_queue_commit();
  1913. if (dot > text && dot[-1] != '\n')
  1914. dot--;
  1915. }
  1916. static void dot_right(void)
  1917. {
  1918. undo_queue_commit();
  1919. if (dot < end - 1 && *dot != '\n')
  1920. dot++;
  1921. }
  1922. static void dot_begin(void)
  1923. {
  1924. undo_queue_commit();
  1925. dot = begin_line(dot); // return pointer to first char cur line
  1926. }
  1927. static void dot_end(void)
  1928. {
  1929. undo_queue_commit();
  1930. dot = end_line(dot); // return pointer to last char cur line
  1931. }
  1932. static char *move_to_col(char *p, int l)
  1933. {
  1934. int co;
  1935. p = begin_line(p);
  1936. co = 0;
  1937. do {
  1938. if (*p == '\n') //vda || *p == '\0')
  1939. break;
  1940. co = next_column(*p, co);
  1941. } while (co <= l && p++ < end);
  1942. return p;
  1943. }
  1944. static void dot_next(void)
  1945. {
  1946. undo_queue_commit();
  1947. dot = next_line(dot);
  1948. }
  1949. static void dot_prev(void)
  1950. {
  1951. undo_queue_commit();
  1952. dot = prev_line(dot);
  1953. }
  1954. static void dot_to_char(int cmd)
  1955. {
  1956. char *q = dot;
  1957. int dir = islower(cmd) ? FORWARD : BACK;
  1958. if (last_search_char == 0)
  1959. return;
  1960. do {
  1961. do {
  1962. q += dir;
  1963. if ((dir == FORWARD ? q > end - 1 : q < text) || *q == '\n') {
  1964. indicate_error();
  1965. return;
  1966. }
  1967. } while (*q != last_search_char);
  1968. } while (--cmdcnt > 0);
  1969. dot = q;
  1970. // place cursor before/after char as required
  1971. if (cmd == 't')
  1972. dot_left();
  1973. else if (cmd == 'T')
  1974. dot_right();
  1975. }
  1976. static void dot_scroll(int cnt, int dir)
  1977. {
  1978. char *q;
  1979. undo_queue_commit();
  1980. for (; cnt > 0; cnt--) {
  1981. if (dir < 0) {
  1982. // scroll Backwards
  1983. // ctrl-Y scroll up one line
  1984. screenbegin = prev_line(screenbegin);
  1985. } else {
  1986. // scroll Forwards
  1987. // ctrl-E scroll down one line
  1988. screenbegin = next_line(screenbegin);
  1989. }
  1990. }
  1991. // make sure "dot" stays on the screen so we dont scroll off
  1992. if (dot < screenbegin)
  1993. dot = screenbegin;
  1994. q = end_screen(); // find new bottom line
  1995. if (dot > q)
  1996. dot = begin_line(q); // is dot is below bottom line?
  1997. dot_skip_over_ws();
  1998. }
  1999. static void dot_skip_over_ws(void)
  2000. {
  2001. // skip WS
  2002. while (isspace((unsigned char)*dot) && *dot != '\n' && dot < end - 1)
  2003. dot++;
  2004. }
  2005. static char *bound_dot(char *p) // make sure text[0] <= P < "end"
  2006. {
  2007. if (p >= end && end > text) {
  2008. p = end - 1;
  2009. indicate_error();
  2010. }
  2011. if (p < text) {
  2012. p = text;
  2013. indicate_error();
  2014. }
  2015. return p;
  2016. }
  2017. //----- Helper Utility Routines --------------------------------
  2018. //----------------------------------------------------------------
  2019. //----- Char Routines --------------------------------------------
  2020. /* Chars that are part of a word-
  2021. * 0123456789_ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz
  2022. * Chars that are Not part of a word (stoppers)
  2023. * !"#$%&'()*+,-./:;<=>?@[\]^`{|}~
  2024. * Chars that are WhiteSpace
  2025. * TAB NEWLINE VT FF RETURN SPACE
  2026. * DO NOT COUNT NEWLINE AS WHITESPACE
  2027. */
  2028. static void new_screen(int ro, int co)
  2029. {
  2030. char *s;
  2031. vi_free(screen);
  2032. screensize = ro * co + 8;
  2033. s = screen = vi_malloc(screensize);
  2034. // initialize the new screen. assume this will be a empty file.
  2035. screen_erase();
  2036. // non-existent text[] lines start with a tilde (~).
  2037. //screen[(1 * co) + 0] = '~';
  2038. //screen[(2 * co) + 0] = '~';
  2039. //..
  2040. //screen[((ro-2) * co) + 0] = '~';
  2041. ro -= 2;
  2042. while (--ro >= 0) {
  2043. s += co;
  2044. *s = '~';
  2045. }
  2046. }
  2047. #if ENABLE_FEATURE_VI_SEARCH
  2048. # if ENABLE_FEATURE_VI_REGEX_SEARCH
  2049. // search for pattern starting at p
  2050. static char *char_search(char *p, const char *pat, int dir_and_range)
  2051. {
  2052. struct re_pattern_buffer preg;
  2053. const char *err;
  2054. char *q;
  2055. int i, size, range, start;
  2056. re_syntax_options = RE_SYNTAX_POSIX_BASIC & (~RE_DOT_NEWLINE);
  2057. if (ignorecase)
  2058. re_syntax_options |= RE_ICASE;
  2059. rt_memset(&preg, 0, sizeof(preg));
  2060. err = re_compile_pattern(pat, strlen(pat), &preg);
  2061. preg.not_bol = p != text;
  2062. preg.not_eol = p != end - 1;
  2063. if (err != NULL) {
  2064. status_line_bold("bad search pattern '%s': %s", pat, err);
  2065. return p;
  2066. }
  2067. range = (dir_and_range & 1);
  2068. q = end - 1; // if FULL
  2069. if (range == LIMITED)
  2070. q = next_line(p);
  2071. if (dir_and_range < 0) { // BACK?
  2072. q = text;
  2073. if (range == LIMITED)
  2074. q = prev_line(p);
  2075. }
  2076. // RANGE could be negative if we are searching backwards
  2077. range = q - p;
  2078. if (range < 0) {
  2079. size = -range;
  2080. start = size;
  2081. } else {
  2082. size = range;
  2083. start = 0;
  2084. }
  2085. q = p - start;
  2086. if (q < text)
  2087. q = text;
  2088. // search for the compiled pattern, preg, in p[]
  2089. // range < 0, start == size: search backward
  2090. // range > 0, start == 0: search forward
  2091. // re_search() < 0: not found or error
  2092. // re_search() >= 0: index of found pattern
  2093. // struct pattern char int int int struct reg
  2094. // re_search(*pattern_buffer, *string, size, start, range, *regs)
  2095. i = re_search(&preg, q, size, start, range, /*struct re_registers*:*/ NULL);
  2096. regfree(&preg);
  2097. return i < 0 ? NULL : q + i;
  2098. }
  2099. # else
  2100. # if ENABLE_FEATURE_VI_SETOPTS
  2101. static int mycmp(const char *s1, const char *s2, int len)
  2102. {
  2103. if (ignorecase) {
  2104. return strncasecmp(s1, s2, len);
  2105. }
  2106. return strncmp(s1, s2, len);
  2107. }
  2108. # else
  2109. # define mycmp strncmp
  2110. # endif
  2111. static char *char_search(char *p, const char *pat, int dir_and_range)
  2112. {
  2113. char *start, *stop;
  2114. int len;
  2115. int range;
  2116. len = strlen(pat);
  2117. range = (dir_and_range & 1);
  2118. if (dir_and_range > 0) { //FORWARD?
  2119. stop = end - 1; // assume range is p..end-1
  2120. if (range == LIMITED)
  2121. stop = next_line(p); // range is to next line
  2122. for (start = p; start < stop; start++) {
  2123. if (mycmp(start, pat, len) == 0) {
  2124. return start;
  2125. }
  2126. }
  2127. } else { //BACK
  2128. stop = text; // assume range is text..p
  2129. if (range == LIMITED)
  2130. stop = prev_line(p); // range is to prev line
  2131. for (start = p - len; start >= stop; start--) {
  2132. if (mycmp(start, pat, len) == 0) {
  2133. return start;
  2134. }
  2135. }
  2136. }
  2137. // pattern not found
  2138. return NULL;
  2139. }
  2140. # endif
  2141. #endif /* FEATURE_VI_SEARCH */
  2142. // find number of characters in indent, p must be at beginning of line
  2143. static size_t indent_len(char *p)
  2144. {
  2145. char *r = p;
  2146. while (r < (end - 1) && isblank(*r))
  2147. r++;
  2148. return r - p;
  2149. }
  2150. static char *char_insert(char *p, char c, int undo) // insert the char c at 'p'
  2151. {
  2152. #if ENABLE_FEATURE_VI_SETOPTS
  2153. size_t len;
  2154. int col, ntab, nspc;
  2155. #endif
  2156. char *bol = begin_line(p);
  2157. if (c == 22) { // Is this an ctrl-V?
  2158. p += stupid_insert(p, '^'); // use ^ to indicate literal next
  2159. refresh(FALSE); // show the ^
  2160. c = get_one_char();
  2161. *p = c;
  2162. #if ENABLE_FEATURE_VI_UNDO
  2163. undo_push_insert(p, 1, undo);
  2164. #else
  2165. modified_count++;
  2166. #endif /* ENABLE_FEATURE_VI_UNDO */
  2167. p++;
  2168. } else if (c == 27) { // Is this an ESC?
  2169. cmd_mode = 0;
  2170. undo_queue_commit();
  2171. cmdcnt = 0;
  2172. end_cmd_q(); // stop adding to q
  2173. last_status_cksum = 0; // force status update
  2174. if ((dot > text) && (p[-1] != '\n')) {
  2175. p--;
  2176. }
  2177. #if ENABLE_FEATURE_VI_SETOPTS
  2178. if (autoindent) {
  2179. len = indent_len(bol);
  2180. if (len && get_column(bol + len) == indentcol && bol[len] == '\n') {
  2181. // remove autoindent from otherwise empty line
  2182. text_hole_delete(bol, bol + len - 1, undo);
  2183. p = bol;
  2184. }
  2185. }
  2186. #endif
  2187. } else if (c == 4) { // ctrl-D reduces indentation
  2188. char *r = bol + indent_len(bol);
  2189. int prev = prev_tabstop(get_column(r));
  2190. while (r > bol && get_column(r) > prev) {
  2191. if (p > bol)
  2192. p--;
  2193. r--;
  2194. r = text_hole_delete(r, r, ALLOW_UNDO_QUEUED);
  2195. }
  2196. #if ENABLE_FEATURE_VI_SETOPTS
  2197. if (autoindent && indentcol && r == end_line(p)) {
  2198. // record changed size of autoindent
  2199. indentcol = get_column(p);
  2200. return p;
  2201. }
  2202. #endif
  2203. #if ENABLE_FEATURE_VI_SETOPTS
  2204. } else if (c == '\t' && expandtab) { // expand tab
  2205. col = get_column(p);
  2206. col = next_tabstop(col) - col + 1;
  2207. while (col--) {
  2208. # if ENABLE_FEATURE_VI_UNDO
  2209. undo_push_insert(p, 1, undo);
  2210. # else
  2211. modified_count++;
  2212. # endif
  2213. p += 1 + stupid_insert(p, ' ');
  2214. }
  2215. #endif
  2216. } else if (
  2217. #ifdef RT_USING_POSIX_TERMIOS // RT-Thread team added
  2218. c == term_orig.c_cc[VERASE] ||
  2219. #endif
  2220. c == 8 || c == 127) { // Is this a BS
  2221. if (p > text) {
  2222. p--;
  2223. p = text_hole_delete(p, p, ALLOW_UNDO_QUEUED); // shrink buffer 1 char
  2224. }
  2225. } else {
  2226. // insert a char into text[]
  2227. if (c == 13)
  2228. c = '\n'; // translate \r to \n
  2229. #if ENABLE_FEATURE_VI_UNDO
  2230. # if ENABLE_FEATURE_VI_UNDO_QUEUE
  2231. if (c == '\n')
  2232. undo_queue_commit();
  2233. # endif
  2234. undo_push_insert(p, 1, undo);
  2235. #else
  2236. modified_count++;
  2237. #endif /* ENABLE_FEATURE_VI_UNDO */
  2238. p += 1 + stupid_insert(p, c); // insert the char
  2239. #if ENABLE_FEATURE_VI_SETOPTS
  2240. if (showmatch && strchr(")]}", c) != NULL) {
  2241. showmatching(p - 1);
  2242. }
  2243. if (autoindent && c == '\n') { // auto indent the new line
  2244. // use indent of current/previous line
  2245. bol = indentcol < 0 ? p : prev_line(p);
  2246. len = indent_len(bol);
  2247. col = get_column(bol + len);
  2248. if (len && col == indentcol) {
  2249. // previous line was empty except for autoindent
  2250. // move the indent to the current line
  2251. memmove(bol + 1, bol, len);
  2252. *bol = '\n';
  2253. return p;
  2254. }
  2255. if (indentcol < 0)
  2256. p--; // open above, indent before newly inserted NL
  2257. if (len) {
  2258. indentcol = col;
  2259. if (expandtab) {
  2260. ntab = 0;
  2261. nspc = col;
  2262. } else {
  2263. ntab = col / tabstop;
  2264. nspc = col % tabstop;
  2265. }
  2266. p += text_hole_make(p, ntab + nspc);
  2267. #if ENABLE_FEATURE_VI_UNDO
  2268. undo_push_insert(p, ntab + nspc, undo);
  2269. #endif
  2270. rt_memset(p, '\t', ntab);
  2271. p += ntab;
  2272. rt_memset(p, ' ', nspc);
  2273. return p + nspc;
  2274. }
  2275. }
  2276. #endif
  2277. }
  2278. #if ENABLE_FEATURE_VI_SETOPTS
  2279. indentcol = 0;
  2280. #endif
  2281. return p;
  2282. }
  2283. // might reallocate text[]! use p += stupid_insert(p, ...),
  2284. // and be careful to not use pointers into potentially freed text[]!
  2285. static uintptr_t stupid_insert(char *p, char c) // stupidly insert the char c at 'p'
  2286. {
  2287. uintptr_t bias;
  2288. bias = text_hole_make(p, 1);
  2289. p += bias;
  2290. *p = c;
  2291. return bias;
  2292. }
  2293. static int at_eof(const char *s)
  2294. {
  2295. // does 's' point to end of file, even with no terminating newline?
  2296. return ((s == end - 2 && s[1] == '\n') || s == end - 1);
  2297. }
  2298. static int find_range(char **start, char **stop, int cmd)
  2299. {
  2300. char *p, *q, *t;
  2301. int buftype = -1;
  2302. int c;
  2303. p = q = dot;
  2304. #if ENABLE_FEATURE_VI_YANKMARK
  2305. if (cmd == 'Y') {
  2306. c = 'y';
  2307. } else
  2308. #endif
  2309. {
  2310. c = get_motion_char();
  2311. }
  2312. #if ENABLE_FEATURE_VI_YANKMARK
  2313. if ((cmd == 'Y' || cmd == c) && strchr("cdy><", c)) {
  2314. #else
  2315. if (cmd == c && strchr("cd><", c)) {
  2316. #endif
  2317. // these cmds operate on whole lines
  2318. buftype = WHOLE;
  2319. if (--cmdcnt > 0) {
  2320. do_cmd('j');
  2321. if (cmd_error)
  2322. buftype = -1;
  2323. }
  2324. } else if (strchr("^%$0bBeEfFtThnN/?|{}\b\177", c)) {
  2325. // Most operate on char positions within a line. Of those that
  2326. // don't '%' needs no special treatment, search commands are
  2327. // marked as MULTI and "{}" are handled below.
  2328. buftype = strchr("nN/?", c) ? MULTI : PARTIAL;
  2329. do_cmd(c); // execute movement cmd
  2330. if (cmd_error)
  2331. buftype = -1;
  2332. if (p == dot) // no movement is an error
  2333. buftype = -1;
  2334. } else if (strchr("wW", c)) {
  2335. buftype = MULTI;
  2336. do_cmd(c); // execute movement cmd
  2337. // step back one char, but not if we're at end of file,
  2338. // or if we are at EOF and search was for 'w' and we're at
  2339. // the start of a 'W' word.
  2340. if (dot > p && (!at_eof(dot) || (c == 'w' && ispunct(*dot))))
  2341. dot--;
  2342. t = dot;
  2343. // don't include trailing WS as part of word
  2344. while (dot > p && isspace(*dot)) {
  2345. if (*dot-- == '\n')
  2346. t = dot;
  2347. }
  2348. // for non-change operations WS after NL is not part of word
  2349. if (cmd != 'c' && dot != t && *dot != '\n')
  2350. dot = t;
  2351. } else if (strchr("GHL+-gjk'\r\n", c)) {
  2352. // these operate on whole lines
  2353. buftype = WHOLE;
  2354. do_cmd(c); // execute movement cmd
  2355. if (cmd_error)
  2356. buftype = -1;
  2357. } else if (c == ' ' || c == 'l') {
  2358. // forward motion by character
  2359. int tmpcnt = (cmdcnt ? cmdcnt : 1);
  2360. buftype = PARTIAL;
  2361. do_cmd(c); // execute movement cmd
  2362. // exclude last char unless range isn't what we expected
  2363. // this indicates we've hit EOL
  2364. if (tmpcnt == dot - p)
  2365. dot--;
  2366. }
  2367. if (buftype == -1) {
  2368. if (c != 27)
  2369. indicate_error();
  2370. return buftype;
  2371. }
  2372. q = dot;
  2373. if (q < p) {
  2374. t = q;
  2375. q = p;
  2376. p = t;
  2377. }
  2378. // movements which don't include end of range
  2379. if (q > p) {
  2380. if (strchr("^0bBFThnN/?|\b\177", c)) {
  2381. q--;
  2382. } else if (strchr("{}", c)) {
  2383. buftype = (p == begin_line(p) && (*q == '\n' || at_eof(q))) ?
  2384. WHOLE : MULTI;
  2385. if (!at_eof(q)) {
  2386. q--;
  2387. if (q > p && p != begin_line(p))
  2388. q--;
  2389. }
  2390. }
  2391. }
  2392. *start = p;
  2393. *stop = q;
  2394. return buftype;
  2395. }
  2396. static int st_test(char *p, int type, int dir, char *tested)
  2397. {
  2398. char c, c0, ci;
  2399. int test, inc;
  2400. inc = dir;
  2401. c = c0 = p[0];
  2402. ci = p[inc];
  2403. test = 0;
  2404. if (type == S_BEFORE_WS) {
  2405. c = ci;
  2406. test = (!isspace((unsigned char)c) || c == '\n');
  2407. }
  2408. if (type == S_TO_WS) {
  2409. c = c0;
  2410. test = (!isspace((unsigned char)c) || c == '\n');
  2411. }
  2412. if (type == S_OVER_WS) {
  2413. c = c0;
  2414. test = isspace((unsigned char)c);
  2415. }
  2416. if (type == S_END_PUNCT) {
  2417. c = ci;
  2418. test = ispunct((unsigned char)c);
  2419. }
  2420. if (type == S_END_ALNUM) {
  2421. c = ci;
  2422. test = (isalnum((unsigned char)c) || c == '_');
  2423. }
  2424. *tested = c;
  2425. return test;
  2426. }
  2427. static char *skip_thing(char *p, int linecnt, int dir, int type)
  2428. {
  2429. char c;
  2430. while (st_test(p, type, dir, &c)) {
  2431. // make sure we limit search to correct number of lines
  2432. if (c == '\n' && --linecnt < 1)
  2433. break;
  2434. if (dir >= 0 && p >= end - 1)
  2435. break;
  2436. if (dir < 0 && p <= text)
  2437. break;
  2438. p += dir; // move to next char
  2439. }
  2440. return p;
  2441. }
  2442. // find matching char of pair () [] {}
  2443. // will crash if c is not one of these
  2444. static char *find_pair(char *p, const char c)
  2445. {
  2446. const char *braces = "()[]{}";
  2447. char match;
  2448. int dir, level;
  2449. dir = strchr(braces, c) - braces;
  2450. dir ^= 1;
  2451. match = braces[dir];
  2452. dir = ((dir & 1) << 1) - 1; /* 1 for ([{, -1 for )\} */
  2453. // look for match, count levels of pairs (( ))
  2454. level = 1;
  2455. for (;;) {
  2456. p += dir;
  2457. if (p < text || p >= end)
  2458. return NULL;
  2459. if (*p == c)
  2460. level++; // increase pair levels
  2461. if (*p == match) {
  2462. level--; // reduce pair level
  2463. if (level == 0)
  2464. return p; // found matching pair
  2465. }
  2466. }
  2467. }
  2468. #if ENABLE_FEATURE_VI_SETOPTS
  2469. // show the matching char of a pair, () [] {}
  2470. static void showmatching(char *p)
  2471. {
  2472. char *q, *save_dot;
  2473. // we found half of a pair
  2474. q = find_pair(p, *p); // get loc of matching char
  2475. if (q == NULL) {
  2476. indicate_error(); // no matching char
  2477. } else {
  2478. // "q" now points to matching pair
  2479. save_dot = dot; // remember where we are
  2480. dot = q; // go to new loc
  2481. refresh(FALSE); // let the user see it
  2482. mysleep(40); // give user some time
  2483. dot = save_dot; // go back to old loc
  2484. refresh(FALSE);
  2485. }
  2486. }
  2487. #endif /* FEATURE_VI_SETOPTS */
  2488. #if ENABLE_FEATURE_VI_UNDO
  2489. static void flush_undo_data(void)
  2490. {
  2491. struct undo_object *undo_entry;
  2492. while (undo_stack_tail) {
  2493. undo_entry = undo_stack_tail;
  2494. undo_stack_tail = undo_entry->prev;
  2495. vi_free(undo_entry);
  2496. }
  2497. }
  2498. // Undo functions and hooks added by Jody Bruchon (jody@jodybruchon.com)
  2499. // Add to the undo stack
  2500. static void undo_push(char *src, unsigned length, int u_type)
  2501. {
  2502. struct undo_object *undo_entry;
  2503. # if ENABLE_FEATURE_VI_UNDO_QUEUE
  2504. int use_spos = u_type & UNDO_USE_SPOS;
  2505. # endif
  2506. // "u_type" values
  2507. // UNDO_INS: insertion, undo will remove from buffer
  2508. // UNDO_DEL: deleted text, undo will restore to buffer
  2509. // UNDO_{INS,DEL}_CHAIN: Same as above but also calls undo_pop() when complete
  2510. // The CHAIN operations are for handling multiple operations that the user
  2511. // performs with a single action, i.e. REPLACE mode or find-and-replace commands
  2512. // UNDO_{INS,DEL}_QUEUED: If queuing feature is enabled, allow use of the queue
  2513. // for the INS/DEL operation.
  2514. // UNDO_{INS,DEL} ORed with UNDO_USE_SPOS: commit the undo queue
  2515. #if ENABLE_FEATURE_VI_UNDO_QUEUE
  2516. // This undo queuing functionality groups multiple character typing or backspaces
  2517. // into a single large undo object. This greatly reduces calls to malloc() for
  2518. // single-character operations while typing and has the side benefit of letting
  2519. // an undo operation remove chunks of text rather than a single character.
  2520. switch (u_type) {
  2521. case UNDO_EMPTY: // Just in case this ever happens...
  2522. return;
  2523. case UNDO_DEL_QUEUED:
  2524. if (length != 1)
  2525. return; // Only queue single characters
  2526. switch (undo_queue_state) {
  2527. case UNDO_EMPTY:
  2528. undo_queue_state = UNDO_DEL;
  2529. case UNDO_DEL:
  2530. undo_queue_spos = src;
  2531. undo_q++;
  2532. undo_queue[CONFIG_FEATURE_VI_UNDO_QUEUE_MAX - undo_q] = *src;
  2533. // If queue is full, dump it into an object
  2534. if (undo_q == CONFIG_FEATURE_VI_UNDO_QUEUE_MAX)
  2535. undo_queue_commit();
  2536. return;
  2537. case UNDO_INS:
  2538. // Switch from storing inserted text to deleted text
  2539. undo_queue_commit();
  2540. undo_push(src, length, UNDO_DEL_QUEUED);
  2541. return;
  2542. }
  2543. break;
  2544. case UNDO_INS_QUEUED:
  2545. if (length < 1)
  2546. return;
  2547. switch (undo_queue_state) {
  2548. case UNDO_EMPTY:
  2549. undo_queue_state = UNDO_INS;
  2550. undo_queue_spos = src;
  2551. case UNDO_INS:
  2552. while (length--) {
  2553. undo_q++; // Don't need to save any data for insertions
  2554. if (undo_q == CONFIG_FEATURE_VI_UNDO_QUEUE_MAX)
  2555. undo_queue_commit();
  2556. }
  2557. return;
  2558. case UNDO_DEL:
  2559. // Switch from storing deleted text to inserted text
  2560. undo_queue_commit();
  2561. undo_push(src, length, UNDO_INS_QUEUED);
  2562. return;
  2563. }
  2564. break;
  2565. }
  2566. #else
  2567. u_type &= ~UNDO_USE_SPOS;
  2568. #endif
  2569. // Allocate a new undo object
  2570. if (u_type == UNDO_DEL || u_type == UNDO_DEL_CHAIN) {
  2571. // For UNDO_DEL objects, save deleted text
  2572. if ((text + length) == end)
  2573. length--;
  2574. // If this deletion empties text[], strip the newline. When the buffer becomes
  2575. // zero-length, a newline is added back, which requires this to compensate.
  2576. undo_entry = vi_zalloc(offsetof(struct undo_object, undo_text) + length);
  2577. rt_memcpy(undo_entry->undo_text, src, length);
  2578. } else {
  2579. undo_entry = vi_zalloc(sizeof(*undo_entry));
  2580. }
  2581. undo_entry->length = length;
  2582. #if ENABLE_FEATURE_VI_UNDO_QUEUE
  2583. if (use_spos) {
  2584. undo_entry->start = undo_queue_spos - text; // use start position from queue
  2585. } else {
  2586. undo_entry->start = src - text; // use offset from start of text buffer
  2587. }
  2588. #else
  2589. undo_entry->start = src - text;
  2590. #endif
  2591. undo_entry->u_type = u_type;
  2592. // Push it on undo stack
  2593. undo_entry->prev = undo_stack_tail;
  2594. undo_stack_tail = undo_entry;
  2595. modified_count++;
  2596. }
  2597. static void undo_push_insert(char *p, int len, int undo)
  2598. {
  2599. switch (undo) {
  2600. case ALLOW_UNDO:
  2601. undo_push(p, len, UNDO_INS);
  2602. break;
  2603. case ALLOW_UNDO_CHAIN:
  2604. undo_push(p, len, UNDO_INS_CHAIN);
  2605. break;
  2606. # if ENABLE_FEATURE_VI_UNDO_QUEUE
  2607. case ALLOW_UNDO_QUEUED:
  2608. undo_push(p, len, UNDO_INS_QUEUED);
  2609. break;
  2610. # endif
  2611. }
  2612. }
  2613. // Undo the last operation
  2614. static void undo_pop(void)
  2615. {
  2616. int repeat;
  2617. char *u_start, *u_end;
  2618. struct undo_object *undo_entry;
  2619. // Commit pending undo queue before popping (should be unnecessary)
  2620. undo_queue_commit();
  2621. undo_entry = undo_stack_tail;
  2622. // Check for an empty undo stack
  2623. if (!undo_entry) {
  2624. status_line("Already at oldest change");
  2625. return;
  2626. }
  2627. switch (undo_entry->u_type) {
  2628. case UNDO_DEL:
  2629. case UNDO_DEL_CHAIN:
  2630. // make hole and put in text that was deleted; deallocate text
  2631. u_start = text + undo_entry->start;
  2632. text_hole_make(u_start, undo_entry->length);
  2633. rt_memcpy(u_start, undo_entry->undo_text, undo_entry->length);
  2634. # if ENABLE_FEATURE_VI_VERBOSE_STATUS
  2635. status_line("Undo [%d] %s %d chars at position %d",
  2636. modified_count, "restored",
  2637. undo_entry->length, undo_entry->start
  2638. );
  2639. # endif
  2640. break;
  2641. case UNDO_INS:
  2642. case UNDO_INS_CHAIN:
  2643. // delete what was inserted
  2644. u_start = undo_entry->start + text;
  2645. u_end = u_start - 1 + undo_entry->length;
  2646. text_hole_delete(u_start, u_end, NO_UNDO);
  2647. # if ENABLE_FEATURE_VI_VERBOSE_STATUS
  2648. status_line("Undo [%d] %s %d chars at position %d",
  2649. modified_count, "deleted",
  2650. undo_entry->length, undo_entry->start
  2651. );
  2652. # endif
  2653. break;
  2654. }
  2655. repeat = 0;
  2656. switch (undo_entry->u_type) {
  2657. // If this is the end of a chain, lower modification count and refresh display
  2658. case UNDO_DEL:
  2659. case UNDO_INS:
  2660. dot = (text + undo_entry->start);
  2661. refresh(FALSE);
  2662. break;
  2663. case UNDO_DEL_CHAIN:
  2664. case UNDO_INS_CHAIN:
  2665. repeat = 1;
  2666. break;
  2667. }
  2668. // Deallocate the undo object we just processed
  2669. undo_stack_tail = undo_entry->prev;
  2670. vi_free(undo_entry);
  2671. modified_count--;
  2672. // For chained operations, continue popping all the way down the chain.
  2673. if (repeat) {
  2674. undo_pop(); // Follow the undo chain if one exists
  2675. }
  2676. }
  2677. #if ENABLE_FEATURE_VI_UNDO_QUEUE
  2678. // Flush any queued objects to the undo stack
  2679. static void undo_queue_commit(void)
  2680. {
  2681. // Pushes the queue object onto the undo stack
  2682. if (undo_q > 0) {
  2683. // Deleted character undo events grow from the end
  2684. undo_push(undo_queue + CONFIG_FEATURE_VI_UNDO_QUEUE_MAX - undo_q,
  2685. undo_q,
  2686. (undo_queue_state | UNDO_USE_SPOS)
  2687. );
  2688. undo_queue_state = UNDO_EMPTY;
  2689. undo_q = 0;
  2690. }
  2691. }
  2692. #endif
  2693. #endif /* ENABLE_FEATURE_VI_UNDO */
  2694. // open a hole in text[]
  2695. // might reallocate text[]! use p += text_hole_make(p, ...),
  2696. // and be careful to not use pointers into potentially freed text[]!
  2697. static uintptr_t text_hole_make(char *p, int size) // at "p", make a 'size' byte hole
  2698. {
  2699. uintptr_t bias = 0;
  2700. if (size <= 0)
  2701. return bias;
  2702. end += size; // adjust the new END
  2703. if (end >= (text + text_size)) {
  2704. char *new_text;
  2705. text_size += end - (text + text_size) + 10240;
  2706. new_text = vi_realloc(text, text_size);
  2707. bias = (new_text - text);
  2708. screenbegin += bias;
  2709. dot += bias;
  2710. end += bias;
  2711. p += bias;
  2712. #if ENABLE_FEATURE_VI_YANKMARK
  2713. {
  2714. int i;
  2715. for (i = 0; i < ARRAY_SIZE(mark); i++)
  2716. if (mark[i])
  2717. mark[i] += bias;
  2718. }
  2719. #endif
  2720. text = new_text;
  2721. }
  2722. memmove(p + size, p, end - size - p);
  2723. rt_memset(p, ' ', size); // clear new hole
  2724. return bias;
  2725. }
  2726. // close a hole in text[]
  2727. // "undo" value indicates if this operation should be undo-able
  2728. static char *text_hole_delete(char *p, char *q, int undo) // delete "p" through "q", inclusive
  2729. {
  2730. char *src, *dest;
  2731. int cnt, hole_size;
  2732. // move forwards, from beginning
  2733. // assume p <= q
  2734. src = q + 1;
  2735. dest = p;
  2736. if (q < p) { // they are backward- swap them
  2737. src = p + 1;
  2738. dest = q;
  2739. }
  2740. hole_size = q - p + 1;
  2741. cnt = end - src;
  2742. #if ENABLE_FEATURE_VI_UNDO
  2743. switch (undo) {
  2744. case NO_UNDO:
  2745. break;
  2746. case ALLOW_UNDO:
  2747. undo_push(p, hole_size, UNDO_DEL);
  2748. break;
  2749. case ALLOW_UNDO_CHAIN:
  2750. undo_push(p, hole_size, UNDO_DEL_CHAIN);
  2751. break;
  2752. # if ENABLE_FEATURE_VI_UNDO_QUEUE
  2753. case ALLOW_UNDO_QUEUED:
  2754. undo_push(p, hole_size, UNDO_DEL_QUEUED);
  2755. break;
  2756. # endif
  2757. }
  2758. modified_count--;
  2759. #endif
  2760. if (src < text || src > end)
  2761. goto thd0;
  2762. if (dest < text || dest >= end)
  2763. goto thd0;
  2764. modified_count++;
  2765. if (src >= end)
  2766. goto thd_atend; // just delete the end of the buffer
  2767. memmove(dest, src, cnt);
  2768. thd_atend:
  2769. end = end - hole_size; // adjust the new END
  2770. if (dest >= end)
  2771. dest = end - 1; // make sure dest in below end-1
  2772. if (end <= text)
  2773. dest = end = text; // keep pointers valid
  2774. thd0:
  2775. return dest;
  2776. }
  2777. // copy text into register, then delete text.
  2778. //
  2779. static char *yank_delete(char *start, char *stop, int buftype, int yf, int undo)
  2780. {
  2781. char *p;
  2782. // make sure start <= stop
  2783. if (start > stop) {
  2784. // they are backwards, reverse them
  2785. p = start;
  2786. start = stop;
  2787. stop = p;
  2788. }
  2789. if (buftype == PARTIAL && *start == '\n')
  2790. return start;
  2791. p = start;
  2792. #if ENABLE_FEATURE_VI_YANKMARK
  2793. text_yank(start, stop, YDreg, buftype);
  2794. #endif
  2795. if (yf == YANKDEL) {
  2796. p = text_hole_delete(start, stop, undo);
  2797. } // delete lines
  2798. return p;
  2799. }
  2800. static void show_help(void)
  2801. {
  2802. rt_kprintf("These features are available:"
  2803. #if ENABLE_FEATURE_VI_SEARCH
  2804. "\n\tPattern searches with / and ?"
  2805. #endif
  2806. #if ENABLE_FEATURE_VI_DOT_CMD
  2807. "\n\tLast command repeat with ."
  2808. #endif
  2809. #if ENABLE_FEATURE_VI_YANKMARK
  2810. "\n\tLine marking with 'x"
  2811. "\n\tNamed buffers with \"x"
  2812. #endif
  2813. #if ENABLE_FEATURE_VI_READONLY
  2814. //not implemented: "\n\tReadonly if vi is called as \"view\""
  2815. //redundant: usage text says this too: "\n\tReadonly with -R command line arg"
  2816. #endif
  2817. #if ENABLE_FEATURE_VI_SET
  2818. "\n\tSome colon mode commands with :"
  2819. #endif
  2820. #if ENABLE_FEATURE_VI_SETOPTS
  2821. "\n\tSettable options with \":set\""
  2822. #endif
  2823. #if ENABLE_FEATURE_VI_USE_SIGNALS
  2824. "\n\tSignal catching- ^C"
  2825. "\n\tJob suspend and resume with ^Z"
  2826. #endif
  2827. #if ENABLE_FEATURE_VI_WIN_RESIZE
  2828. "\n\tAdapt to window re-sizes"
  2829. #endif
  2830. );
  2831. }
  2832. #if ENABLE_FEATURE_VI_DOT_CMD
  2833. static void start_new_cmd_q(char c)
  2834. {
  2835. // get buffer for new cmd
  2836. dotcnt = cmdcnt ? cmdcnt : 1;
  2837. last_modifying_cmd[0] = c;
  2838. lmc_len = 1;
  2839. adding2q = 1;
  2840. }
  2841. static void end_cmd_q(void)
  2842. {
  2843. #if ENABLE_FEATURE_VI_YANKMARK
  2844. YDreg = 26; // go back to default Yank/Delete reg
  2845. #endif
  2846. adding2q = 0;
  2847. }
  2848. #endif /* FEATURE_VI_DOT_CMD */
  2849. #if ENABLE_FEATURE_VI_YANKMARK \
  2850. || (ENABLE_FEATURE_VI_COLON && ENABLE_FEATURE_VI_SEARCH) \
  2851. || ENABLE_FEATURE_VI_CRASHME
  2852. // might reallocate text[]! use p += string_insert(p, ...),
  2853. // and be careful to not use pointers into potentially freed text[]!
  2854. static uintptr_t string_insert(char *p, const char *s, int undo) // insert the string at 'p'
  2855. {
  2856. uintptr_t bias;
  2857. int i;
  2858. i = strlen(s);
  2859. #if ENABLE_FEATURE_VI_UNDO
  2860. undo_push_insert(p, i, undo);
  2861. #endif
  2862. bias = text_hole_make(p, i);
  2863. p += bias;
  2864. rt_memcpy(p, s, i);
  2865. return bias;
  2866. }
  2867. #endif
  2868. //----- Block insert/delete, undo ops --------------------------
  2869. #if ENABLE_FEATURE_VI_YANKMARK
  2870. // copy text into a register
  2871. static char *text_yank(char *p, char *q, int dest, int buftype)
  2872. {
  2873. char *oldreg = reg[dest];
  2874. int cnt = q - p;
  2875. if (cnt < 0) { // they are backwards- reverse them
  2876. p = q;
  2877. cnt = -cnt;
  2878. }
  2879. // Don't free register yet. This prevents the memory allocator
  2880. // from reusing the free block so we can detect if it's changed.
  2881. reg[dest] = vi_strndup(p, cnt + 1);
  2882. vi_free(oldreg);
  2883. return p;
  2884. }
  2885. static char what_reg(void)
  2886. {
  2887. char c;
  2888. c = 'D'; // default to D-reg
  2889. if (YDreg <= 25)
  2890. c = 'a' + (char) YDreg;
  2891. if (YDreg == 26)
  2892. c = 'D';
  2893. if (YDreg == 27)
  2894. c = 'U';
  2895. return c;
  2896. }
  2897. static void check_context(char cmd)
  2898. {
  2899. // Certain movement commands update the context.
  2900. if (strchr(":%{}'GHLMz/?Nn", cmd) != NULL) {
  2901. mark[27] = mark[26]; // move cur to prev
  2902. mark[26] = dot; // move local to cur
  2903. }
  2904. }
  2905. static char *swap_context(char *p) // goto new context for '' command make this the current context
  2906. {
  2907. char *tmp;
  2908. // the current context is in mark[26]
  2909. // the previous context is in mark[27]
  2910. // only swap context if other context is valid
  2911. if (text <= mark[27] && mark[27] <= end - 1) {
  2912. tmp = mark[27];
  2913. mark[27] = mark[26];
  2914. mark[26] = tmp;
  2915. p = mark[26]; // where we are going- previous context
  2916. }
  2917. return p;
  2918. }
  2919. # if ENABLE_FEATURE_VI_VERBOSE_STATUS
  2920. static void yank_status(const char *op, const char *p, int cnt)
  2921. {
  2922. int lines, chars;
  2923. lines = chars = 0;
  2924. while (*p) {
  2925. ++chars;
  2926. if (*p++ == '\n')
  2927. ++lines;
  2928. }
  2929. status_line("%s %d lines (%d chars) from [%c]",
  2930. op, lines * cnt, chars * cnt, what_reg());
  2931. }
  2932. # endif
  2933. #endif /* FEATURE_VI_YANKMARK */
  2934. //----- Set terminal attributes --------------------------------
  2935. static void rawmode(void)
  2936. {
  2937. #ifdef RT_USING_POSIX_TERMIOS // RT-Thread team added
  2938. // no TERMIOS_CLEAR_ISIG: leave ISIG on - allow signals
  2939. set_termios_to_raw(STDIN_FILENO, &term_orig, TERMIOS_RAW_CRNL);
  2940. #endif
  2941. }
  2942. static void cookmode(void)
  2943. {
  2944. fflush_all();
  2945. #ifdef RT_USING_POSIX_TERMIOS // RT-Thread team added
  2946. tcsetattr_stdin_TCSANOW(&term_orig);
  2947. #endif
  2948. }
  2949. #if ENABLE_FEATURE_VI_USE_SIGNALS
  2950. static void winch_handler(int sig UNUSED_PARAM)
  2951. {
  2952. int save_errno = errno;
  2953. // FIXME: do it in main loop!!!
  2954. signal(SIGWINCH, winch_handler);
  2955. query_screen_dimensions();
  2956. new_screen(rows, columns); // get memory for virtual screen
  2957. redraw(TRUE); // re-draw the screen
  2958. errno = save_errno;
  2959. }
  2960. static void tstp_handler(int sig UNUSED_PARAM)
  2961. {
  2962. int save_errno = errno;
  2963. // ioctl inside cookmode() was seen to generate SIGTTOU,
  2964. // stopping us too early. Prevent that:
  2965. signal(SIGTTOU, SIG_IGN);
  2966. go_bottom_and_clear_to_eol();
  2967. cookmode(); // terminal to "cooked"
  2968. // stop now
  2969. //signal(SIGTSTP, SIG_DFL);
  2970. //raise(SIGTSTP);
  2971. raise(SIGSTOP); // avoid "dance" with TSTP handler - use SIGSTOP instead
  2972. //signal(SIGTSTP, tstp_handler);
  2973. // we have been "continued" with SIGCONT, restore screen and termios
  2974. rawmode(); // terminal to "raw"
  2975. last_status_cksum = 0; // force status update
  2976. redraw(TRUE); // re-draw the screen
  2977. errno = save_errno;
  2978. }
  2979. static void int_handler(int sig)
  2980. {
  2981. signal(SIGINT, int_handler);
  2982. siglongjmp(restart, sig);
  2983. }
  2984. #endif /* FEATURE_VI_USE_SIGNALS */
  2985. // sleep for 'h' 1/100 seconds, return 1/0 if stdin is (ready for read)/(not ready)
  2986. static int mysleep(int hund)
  2987. {
  2988. struct pollfd pfd[1];
  2989. if (hund != 0)
  2990. fflush_all();
  2991. pfd[0].fd = STDIN_FILENO;
  2992. pfd[0].events = POLLIN;
  2993. return safe_poll(pfd, 1, hund*10) > 0;
  2994. }
  2995. //----- IO Routines --------------------------------------------
  2996. static int readit(void) // read (maybe cursor) key from stdin
  2997. {
  2998. int c;
  2999. fflush_all();
  3000. // Wait for input. TIMEOUT = -1 makes read_key wait even
  3001. // on nonblocking stdin.
  3002. // Note: read_key sets errno to 0 on success.
  3003. again:
  3004. c = read_key(STDIN_FILENO, readbuffer, /*timeout:*/ -1);
  3005. if (c == -1) { // EOF/error
  3006. if (errno == EAGAIN) // paranoia
  3007. goto again;
  3008. go_bottom_and_clear_to_eol();
  3009. cookmode(); // terminal to "cooked"
  3010. rt_kprintf("can't read user input");
  3011. }
  3012. return c;
  3013. }
  3014. #if ENABLE_FEATURE_VI_DOT_CMD
  3015. static int get_one_char(void)
  3016. {
  3017. int c;
  3018. if (!adding2q) {
  3019. // we are not adding to the q.
  3020. // but, we may be reading from a saved q.
  3021. // (checking "ioq" for NULL is wrong, it's not reset to NULL
  3022. // when done - "ioq_start" is reset instead).
  3023. if (ioq_start != NULL) {
  3024. // there is a queue to get chars from.
  3025. // careful with correct sign expansion!
  3026. c = (unsigned char)*ioq++;
  3027. if (c != '\0')
  3028. return c;
  3029. // the end of the q
  3030. vi_free(ioq_start);
  3031. ioq_start = NULL;
  3032. // read from STDIN:
  3033. }
  3034. return readit();
  3035. }
  3036. // we are adding STDIN chars to q.
  3037. c = readit();
  3038. if (lmc_len >= ARRAY_SIZE(last_modifying_cmd) - 2) {
  3039. // last_modifying_cmd[] is too small, can't remember the cmd
  3040. // - drop it
  3041. adding2q = 0;
  3042. lmc_len = 0;
  3043. } else {
  3044. last_modifying_cmd[lmc_len++] = c;
  3045. }
  3046. return c;
  3047. }
  3048. #endif
  3049. // Get type of thing to operate on and adjust count
  3050. static int get_motion_char(void)
  3051. {
  3052. int c, cnt;
  3053. c = get_one_char();
  3054. if (isdigit(c)) {
  3055. if (c != '0') {
  3056. // get any non-zero motion count
  3057. for (cnt = 0; isdigit(c); c = get_one_char())
  3058. cnt = cnt * 10 + (c - '0');
  3059. cmdcnt = (cmdcnt ? cmdcnt : 1) * cnt;
  3060. } else {
  3061. // ensure standalone '0' works
  3062. cmdcnt = 0;
  3063. }
  3064. }
  3065. return c;
  3066. }
  3067. // Get input line (uses "status line" area)
  3068. static char *get_input_line(const char *prompt)
  3069. {
  3070. // char [MAX_INPUT_LEN]
  3071. #define buf get_input_line__buf
  3072. int c;
  3073. int i;
  3074. strcpy(buf, prompt);
  3075. last_status_cksum = 0; // force status update
  3076. go_bottom_and_clear_to_eol();
  3077. write1(buf); // write out the :, /, or ? prompt
  3078. i = strlen(buf);
  3079. while (i < MAX_INPUT_LEN - 1) {
  3080. c = get_one_char();
  3081. if (c == '\n' || c == '\r' || c == 27)
  3082. break; // this is end of input
  3083. if (
  3084. #ifdef RT_USING_POSIX_TERMIOS // RT-Thread team added
  3085. c == term_orig.c_cc[VERASE] ||
  3086. #endif
  3087. c == 8 || c == 127) {
  3088. // user wants to erase prev char
  3089. write1("\b \b"); // erase char on screen
  3090. buf[--i] = '\0';
  3091. if (i <= 0) // user backs up before b-o-l, exit
  3092. break;
  3093. } else if (c > 0 && c < 256) { // exclude Unicode
  3094. // (TODO: need to handle Unicode)
  3095. buf[i] = c;
  3096. buf[++i] = '\0';
  3097. vi_putchar(c);
  3098. }
  3099. }
  3100. refresh(FALSE);
  3101. return buf;
  3102. #undef buf
  3103. }
  3104. // might reallocate text[]!
  3105. static int file_insert(const char *fn, char *p, int initial)
  3106. {
  3107. int cnt = -1;
  3108. int fd, size;
  3109. struct stat statbuf;
  3110. if (p < text || p > end) {
  3111. status_line_bold("Trying to insert file outside of memory");
  3112. return cnt;
  3113. }
  3114. fd = open(fn, O_RDONLY, 0);
  3115. if (fd < 0) {
  3116. if (!initial)
  3117. status_line_bold_errno(fn);
  3118. return cnt;
  3119. }
  3120. /* Validate file */
  3121. if (fstat(fd, &statbuf) < 0) {
  3122. status_line_bold_errno(fn);
  3123. goto fi;
  3124. }
  3125. if (!S_ISREG(statbuf.st_mode)) {
  3126. status_line_bold("'%s' is not a regular file", fn);
  3127. goto fi;
  3128. }
  3129. size = (statbuf.st_size < INT_MAX ? (int)statbuf.st_size : INT_MAX);
  3130. p += text_hole_make(p, size);
  3131. cnt = full_read(fd, p, size);
  3132. if (cnt < 0) {
  3133. status_line_bold_errno(fn);
  3134. p = text_hole_delete(p, p + size - 1, NO_UNDO); // un-do buffer insert
  3135. } else if (cnt < size) {
  3136. // There was a partial read, shrink unused space
  3137. p = text_hole_delete(p + cnt, p + size - 1, NO_UNDO);
  3138. status_line_bold("can't read '%s'", fn);
  3139. }
  3140. # if ENABLE_FEATURE_VI_UNDO
  3141. else {
  3142. undo_push_insert(p, size, ALLOW_UNDO);
  3143. }
  3144. # endif
  3145. fi:
  3146. close(fd);
  3147. #if ENABLE_FEATURE_VI_READONLY
  3148. if (initial
  3149. && ((access(fn, W_OK) < 0) ||
  3150. /* root will always have access()
  3151. * so we check fileperms too */
  3152. !(statbuf.st_mode & (S_IWUSR | S_IWGRP | S_IWOTH))
  3153. )
  3154. ) {
  3155. SET_READONLY_FILE(readonly_mode);
  3156. }
  3157. #endif
  3158. return cnt;
  3159. }
  3160. static int file_write(char *fn, char *first, char *last)
  3161. {
  3162. int fd, cnt, charcnt;
  3163. if (fn == 0) {
  3164. status_line_bold("No current filename");
  3165. return -2;
  3166. }
  3167. /* By popular request we do not open file with O_TRUNC,
  3168. * but instead ftruncate() it _after_ successful write.
  3169. * Might reduce amount of data lost on power fail etc.
  3170. */
  3171. // ftruncate nosys + O_TRUNC
  3172. fd = open(fn, (O_WRONLY | O_CREAT | O_TRUNC), 0666);
  3173. if (fd < 0)
  3174. return -1;
  3175. cnt = last - first + 1;
  3176. charcnt = full_write(fd, first, cnt);
  3177. // ftruncate(fd, charcnt);
  3178. if (charcnt == cnt) {
  3179. // good write
  3180. //modified_count = FALSE;
  3181. } else {
  3182. charcnt = 0;
  3183. }
  3184. close(fd);
  3185. return charcnt;
  3186. }
  3187. //----- Terminal Drawing ---------------------------------------
  3188. // The terminal is made up of 'rows' line of 'columns' columns.
  3189. // classically this would be 24 x 80.
  3190. // screen coordinates
  3191. // 0,0 ... 0,79
  3192. // 1,0 ... 1,79
  3193. // . ... .
  3194. // . ... .
  3195. // 22,0 ... 22,79
  3196. // 23,0 ... 23,79 <- status line
  3197. //----- Move the cursor to row x col (count from 0, not 1) -------
  3198. static void place_cursor(int row, int col)
  3199. {
  3200. char cm1[sizeof(ESC_SET_CURSOR_POS) + sizeof(int)*3 * 2];
  3201. if (row < 0) row = 0;
  3202. if (row >= rows) row = rows - 1;
  3203. if (col < 0) col = 0;
  3204. if (col >= columns) col = columns - 1;
  3205. rt_sprintf(cm1, ESC_SET_CURSOR_POS, row + 1, col + 1);
  3206. write1(cm1);
  3207. }
  3208. //----- Erase from cursor to end of line -----------------------
  3209. static void clear_to_eol(void)
  3210. {
  3211. write1(ESC_CLEAR2EOL);
  3212. }
  3213. static void go_bottom_and_clear_to_eol(void)
  3214. {
  3215. place_cursor(rows - 1, 0);
  3216. clear_to_eol();
  3217. }
  3218. //----- Start standout mode ------------------------------------
  3219. static void standout_start(void)
  3220. {
  3221. write1(ESC_BOLD_TEXT);
  3222. }
  3223. //----- End standout mode --------------------------------------
  3224. static void standout_end(void)
  3225. {
  3226. write1(ESC_NORM_TEXT);
  3227. }
  3228. //----- Flash the screen --------------------------------------
  3229. static void flash(int h)
  3230. {
  3231. standout_start();
  3232. redraw(TRUE);
  3233. mysleep(h);
  3234. standout_end();
  3235. redraw(TRUE);
  3236. }
  3237. static void indicate_error(void)
  3238. {
  3239. #if ENABLE_FEATURE_VI_CRASHME
  3240. if (crashme > 0)
  3241. return;
  3242. #endif
  3243. cmd_error = TRUE;
  3244. if (!err_method) {
  3245. write1(ESC_BELL);
  3246. } else {
  3247. flash(10);
  3248. }
  3249. }
  3250. //----- Screen[] Routines --------------------------------------
  3251. //----- Erase the Screen[] memory ------------------------------
  3252. static void screen_erase(void)
  3253. {
  3254. rt_memset(screen, ' ', screensize); // clear new screen
  3255. }
  3256. static int bufsum(char *buf, int count)
  3257. {
  3258. int sum = 0;
  3259. char *e = buf + count;
  3260. while (buf < e)
  3261. sum += (unsigned char) *buf++;
  3262. return sum;
  3263. }
  3264. //----- Draw the status line at bottom of the screen -------------
  3265. static void show_status_line(void)
  3266. {
  3267. int cnt = 0, cksum = 0;
  3268. // either we already have an error or status message, or we
  3269. // create one.
  3270. if (!have_status_msg) {
  3271. cnt = format_edit_status();
  3272. cksum = bufsum(status_buffer, cnt);
  3273. }
  3274. if (have_status_msg || ((cnt > 0 && last_status_cksum != cksum))) {
  3275. last_status_cksum = cksum; // remember if we have seen this line
  3276. go_bottom_and_clear_to_eol();
  3277. write1(status_buffer);
  3278. if (have_status_msg) {
  3279. if (((int)strlen(status_buffer) - (have_status_msg - 1)) >
  3280. (columns - 1) ) {
  3281. have_status_msg = 0;
  3282. Hit_Return();
  3283. }
  3284. have_status_msg = 0;
  3285. }
  3286. place_cursor(crow, ccol); // put cursor back in correct place
  3287. }
  3288. fflush_all();
  3289. }
  3290. //----- format the status buffer, the bottom line of screen ------
  3291. // format status buffer, with STANDOUT mode
  3292. static void status_line_bold(const char *format, ...)
  3293. {
  3294. va_list args;
  3295. va_start(args, format);
  3296. strcpy(status_buffer, ESC_BOLD_TEXT);
  3297. rt_vsnprintf(status_buffer + (sizeof(ESC_BOLD_TEXT)-1),
  3298. STATUS_BUFFER_LEN - sizeof(ESC_BOLD_TEXT) - sizeof(ESC_NORM_TEXT),
  3299. format, args
  3300. );
  3301. strcat(status_buffer, ESC_NORM_TEXT);
  3302. va_end(args);
  3303. have_status_msg = 1 + (sizeof(ESC_BOLD_TEXT)-1) + (sizeof(ESC_NORM_TEXT)-1);
  3304. }
  3305. static void status_line_bold_errno(const char *fn)
  3306. {
  3307. status_line_bold("'%s' %s", fn, strerror(errno));
  3308. }
  3309. //----- format the status buffer, the bottom line of screen ------
  3310. static void status_line(const char *format, ...)
  3311. {
  3312. va_list args;
  3313. va_start(args, format);
  3314. rt_vsnprintf(status_buffer, STATUS_BUFFER_LEN, format, args);
  3315. va_end(args);
  3316. have_status_msg = 1;
  3317. }
  3318. // copy s to buf, convert unprintable
  3319. static void print_literal(char *buf, const char *s)
  3320. {
  3321. char *d;
  3322. unsigned char c;
  3323. if (!s[0])
  3324. s = "(NULL)";
  3325. d = buf;
  3326. for (; *s; s++) {
  3327. c = *s;
  3328. if ((c & 0x80) && !Isprint(c))
  3329. c = '?';
  3330. if (c < ' ' || c == 0x7f) {
  3331. *d++ = '^';
  3332. c |= '@'; /* 0x40 */
  3333. if (c == 0x7f)
  3334. c = '?';
  3335. }
  3336. *d++ = c;
  3337. *d = '\0';
  3338. if (d - buf > MAX_INPUT_LEN - 10) // paranoia
  3339. break;
  3340. }
  3341. }
  3342. static void not_implemented(const char *s)
  3343. {
  3344. char buf[MAX_INPUT_LEN];
  3345. print_literal(buf, s);
  3346. status_line_bold("'%s' is not implemented", buf);
  3347. }
  3348. // show file status on status line
  3349. static int format_edit_status(void)
  3350. {
  3351. static const char cmd_mode_indicator[] ALIGN1 = "-IR-";
  3352. #define tot format_edit_status__tot
  3353. int cur, percent, ret, trunc_at;
  3354. // modified_count is now a counter rather than a flag. this
  3355. // helps reduce the amount of line counting we need to do.
  3356. // (this will cause a mis-reporting of modified status
  3357. // once every MAXINT editing operations.)
  3358. // it would be nice to do a similar optimization here -- if
  3359. // we haven't done a motion that could have changed which line
  3360. // we're on, then we shouldn't have to do this count_lines()
  3361. cur = count_lines(text, dot);
  3362. // count_lines() is expensive.
  3363. // Call it only if something was changed since last time
  3364. // we were here:
  3365. if (modified_count != last_modified_count) {
  3366. tot = cur + count_lines(dot, end - 1) - 1;
  3367. last_modified_count = modified_count;
  3368. }
  3369. // current line percent
  3370. // ------------- ~~ ----------
  3371. // total lines 100
  3372. if (tot > 0) {
  3373. percent = (100 * cur) / tot;
  3374. } else {
  3375. cur = tot = 0;
  3376. percent = 100;
  3377. }
  3378. trunc_at = columns < STATUS_BUFFER_LEN-1 ?
  3379. columns : STATUS_BUFFER_LEN-1;
  3380. ret = rt_snprintf(status_buffer, trunc_at+1,
  3381. #if ENABLE_FEATURE_VI_READONLY
  3382. "%c %s%s%s %d/%d %d%%",
  3383. #else
  3384. "%c %s%s %d/%d %d%%",
  3385. #endif
  3386. cmd_mode_indicator[cmd_mode & 3],
  3387. (current_filename != NULL ? current_filename : "No file"),
  3388. #if ENABLE_FEATURE_VI_READONLY
  3389. (readonly_mode ? " [Readonly]" : ""),
  3390. #endif
  3391. (modified_count ? " [Modified]" : ""),
  3392. cur, tot, percent);
  3393. if (ret >= 0 && ret < trunc_at)
  3394. return ret; /* it all fit */
  3395. return trunc_at; /* had to truncate */
  3396. #undef tot
  3397. }
  3398. //----- Force refresh of all Lines -----------------------------
  3399. static void redraw(int full_screen)
  3400. {
  3401. // cursor to top,left; clear to the end of screen
  3402. write1(ESC_SET_CURSOR_TOPLEFT ESC_CLEAR2EOS);
  3403. screen_erase(); // erase the internal screen buffer
  3404. last_status_cksum = 0; // force status update
  3405. refresh(full_screen); // this will redraw the entire display
  3406. show_status_line();
  3407. }
  3408. //----- Format a text[] line into a buffer ---------------------
  3409. static char *format_line(char *src /*, int li*/)
  3410. {
  3411. unsigned char c;
  3412. int co;
  3413. int ofs = offset;
  3414. char *dest = scr_out_buf; // [MAX_SCR_COLS + MAX_TABSTOP * 2]
  3415. c = '~'; // char in col 0 in non-existent lines is '~'
  3416. co = 0;
  3417. while (co < columns + tabstop) {
  3418. // have we gone past the end?
  3419. if (src < end) {
  3420. c = *src++;
  3421. if (c == '\n')
  3422. break;
  3423. if ((c & 0x80) && !Isprint(c)) {
  3424. c = '.';
  3425. }
  3426. if (c < ' ' || c == 0x7f) {
  3427. if (c == '\t') {
  3428. c = ' ';
  3429. // co % 8 != 7
  3430. while ((co % tabstop) != (tabstop - 1)) {
  3431. dest[co++] = c;
  3432. }
  3433. } else {
  3434. dest[co++] = '^';
  3435. if (c == 0x7f)
  3436. c = '?';
  3437. else
  3438. c += '@'; // Ctrl-X -> 'X'
  3439. }
  3440. }
  3441. }
  3442. dest[co++] = c;
  3443. // discard scrolled-off-to-the-left portion,
  3444. // in tabstop-sized pieces
  3445. if (ofs >= tabstop && co >= tabstop) {
  3446. memmove(dest, dest + tabstop, co);
  3447. co -= tabstop;
  3448. ofs -= tabstop;
  3449. }
  3450. if (src >= end)
  3451. break;
  3452. }
  3453. // check "short line, gigantic offset" case
  3454. if (co < ofs)
  3455. ofs = co;
  3456. // discard last scrolled off part
  3457. co -= ofs;
  3458. dest += ofs;
  3459. // fill the rest with spaces
  3460. if (co < columns)
  3461. rt_memset(&dest[co], ' ', columns - co);
  3462. return dest;
  3463. }
  3464. //----- Refresh the changed screen lines -----------------------
  3465. // Copy the source line from text[] into the buffer and note
  3466. // if the current screenline is different from the new buffer.
  3467. // If they differ then that line needs redrawing on the terminal.
  3468. //
  3469. static void refresh(int full_screen)
  3470. {
  3471. #define old_offset refresh__old_offset
  3472. int li, changed;
  3473. char *tp, *sp; // pointer into text[] and screen[]
  3474. if (ENABLE_FEATURE_VI_WIN_RESIZE IF_FEATURE_VI_ASK_TERMINAL(&& !G.get_rowcol_error) ) {
  3475. unsigned c = columns, r = rows;
  3476. query_screen_dimensions();
  3477. #if ENABLE_FEATURE_VI_USE_SIGNALS
  3478. full_screen |= (c - columns) | (r - rows);
  3479. #else
  3480. if (c != columns || r != rows) {
  3481. full_screen = TRUE;
  3482. /* update screen memory since SIGWINCH won't have done it */
  3483. new_screen(rows, columns);
  3484. }
  3485. #endif
  3486. }
  3487. sync_cursor(dot, &crow, &ccol); // where cursor will be (on "dot")
  3488. tp = screenbegin; // index into text[] of top line
  3489. // compare text[] to screen[] and mark screen[] lines that need updating
  3490. for (li = 0; li < rows - 1; li++) {
  3491. int cs, ce; // column start & end
  3492. char *out_buf;
  3493. // format current text line
  3494. out_buf = format_line(tp /*, li*/);
  3495. // skip to the end of the current text[] line
  3496. if (tp < end) {
  3497. char *t = memchr(tp, '\n', end - tp);
  3498. if (!t) t = end - 1;
  3499. tp = t + 1;
  3500. }
  3501. // see if there are any changes between vitual screen and out_buf
  3502. changed = FALSE; // assume no change
  3503. cs = 0;
  3504. ce = columns - 1;
  3505. sp = &screen[li * columns]; // start of screen line
  3506. if (full_screen) {
  3507. // force re-draw of every single column from 0 - columns-1
  3508. goto re0;
  3509. }
  3510. // compare newly formatted buffer with virtual screen
  3511. // look forward for first difference between buf and screen
  3512. for (; cs <= ce; cs++) {
  3513. if (out_buf[cs] != sp[cs]) {
  3514. changed = TRUE; // mark for redraw
  3515. break;
  3516. }
  3517. }
  3518. // look backward for last difference between out_buf and screen
  3519. for (; ce >= cs; ce--) {
  3520. if (out_buf[ce] != sp[ce]) {
  3521. changed = TRUE; // mark for redraw
  3522. break;
  3523. }
  3524. }
  3525. // now, cs is index of first diff, and ce is index of last diff
  3526. // if horz offset has changed, force a redraw
  3527. if (offset != old_offset) {
  3528. re0:
  3529. changed = TRUE;
  3530. }
  3531. // make a sanity check of columns indexes
  3532. if (cs < 0) cs = 0;
  3533. if (ce > columns - 1) ce = columns - 1;
  3534. if (cs > ce) { cs = 0; ce = columns - 1; }
  3535. // is there a change between vitual screen and out_buf
  3536. if (changed) {
  3537. // copy changed part of buffer to virtual screen
  3538. rt_memcpy(sp+cs, out_buf+cs, ce-cs+1);
  3539. place_cursor(li, cs);
  3540. // write line out to terminal
  3541. write(STDOUT_FILENO, &sp[cs], ce - cs + 1); /* fwrite(&sp[cs], ce - cs + 1, 1, stdout); */
  3542. }
  3543. }
  3544. place_cursor(crow, ccol);
  3545. if (!keep_index)
  3546. cindex = ccol + offset;
  3547. old_offset = offset;
  3548. #undef old_offset
  3549. }
  3550. //---------------------------------------------------------------------
  3551. //----- the Ascii Chart -----------------------------------------------
  3552. // 00 nul 01 soh 02 stx 03 etx 04 eot 05 enq 06 ack 07 bel
  3553. // 08 bs 09 ht 0a nl 0b vt 0c np 0d cr 0e so 0f si
  3554. // 10 dle 11 dc1 12 dc2 13 dc3 14 dc4 15 nak 16 syn 17 etb
  3555. // 18 can 19 em 1a sub 1b esc 1c fs 1d gs 1e rs 1f us
  3556. // 20 sp 21 ! 22 " 23 # 24 $ 25 % 26 & 27 '
  3557. // 28 ( 29 ) 2a * 2b + 2c , 2d - 2e . 2f /
  3558. // 30 0 31 1 32 2 33 3 34 4 35 5 36 6 37 7
  3559. // 38 8 39 9 3a : 3b ; 3c < 3d = 3e > 3f ?
  3560. // 40 @ 41 A 42 B 43 C 44 D 45 E 46 F 47 G
  3561. // 48 H 49 I 4a J 4b K 4c L 4d M 4e N 4f O
  3562. // 50 P 51 Q 52 R 53 S 54 T 55 U 56 V 57 W
  3563. // 58 X 59 Y 5a Z 5b [ 5c \ 5d ] 5e ^ 5f _
  3564. // 60 ` 61 a 62 b 63 c 64 d 65 e 66 f 67 g
  3565. // 68 h 69 i 6a j 6b k 6c l 6d m 6e n 6f o
  3566. // 70 p 71 q 72 r 73 s 74 t 75 u 76 v 77 w
  3567. // 78 x 79 y 7a z 7b { 7c | 7d } 7e ~ 7f del
  3568. //---------------------------------------------------------------------
  3569. //----- Execute a Vi Command -----------------------------------
  3570. static void do_cmd(int c)
  3571. {
  3572. char *p, *q, *save_dot;
  3573. char buf[12];
  3574. int dir;
  3575. int cnt, i, j;
  3576. int c1;
  3577. #if ENABLE_FEATURE_VI_YANKMARK
  3578. char *orig_dot = dot;
  3579. #endif
  3580. #if ENABLE_FEATURE_VI_UNDO
  3581. int allow_undo = ALLOW_UNDO;
  3582. int undo_del = UNDO_DEL;
  3583. #endif
  3584. // c1 = c; // quiet the compiler
  3585. // cnt = yf = 0; // quiet the compiler
  3586. // p = q = save_dot = buf; // quiet the compiler
  3587. rt_memset(buf, '\0', sizeof(buf));
  3588. keep_index = FALSE;
  3589. cmd_error = FALSE;
  3590. show_status_line();
  3591. // if this is a cursor key, skip these checks
  3592. switch (c) {
  3593. case KEYCODE_UP:
  3594. case KEYCODE_DOWN:
  3595. case KEYCODE_LEFT:
  3596. case KEYCODE_RIGHT:
  3597. case KEYCODE_HOME:
  3598. case KEYCODE_END:
  3599. case KEYCODE_PAGEUP:
  3600. case KEYCODE_PAGEDOWN:
  3601. case KEYCODE_DELETE:
  3602. goto key_cmd_mode;
  3603. }
  3604. if (cmd_mode == 2) {
  3605. // flip-flop Insert/Replace mode
  3606. if (c == KEYCODE_INSERT)
  3607. goto dc_i;
  3608. // we are 'R'eplacing the current *dot with new char
  3609. if (*dot == '\n') {
  3610. // don't Replace past E-o-l
  3611. cmd_mode = 1; // convert to insert
  3612. undo_queue_commit();
  3613. } else {
  3614. if (1 <= c || Isprint(c)) {
  3615. if (c != 27)
  3616. dot = yank_delete(dot, dot, PARTIAL, YANKDEL, ALLOW_UNDO); // delete char
  3617. dot = char_insert(dot, c, ALLOW_UNDO_CHAIN); // insert new char
  3618. }
  3619. goto dc1;
  3620. }
  3621. }
  3622. if (cmd_mode == 1) {
  3623. // hitting "Insert" twice means "R" replace mode
  3624. if (c == KEYCODE_INSERT) goto dc5;
  3625. // insert the char c at "dot"
  3626. if (1 <= c || Isprint(c)) {
  3627. dot = char_insert(dot, c, ALLOW_UNDO_QUEUED);
  3628. }
  3629. goto dc1;
  3630. }
  3631. key_cmd_mode:
  3632. switch (c) {
  3633. //case 0x01: // soh
  3634. //case 0x09: // ht
  3635. //case 0x0b: // vt
  3636. //case 0x0e: // so
  3637. //case 0x0f: // si
  3638. //case 0x10: // dle
  3639. //case 0x11: // dc1
  3640. //case 0x13: // dc3
  3641. #if ENABLE_FEATURE_VI_CRASHME
  3642. case 0x14: // dc4 ctrl-T
  3643. crashme = (crashme == 0) ? 1 : 0;
  3644. break;
  3645. #endif
  3646. //case 0x16: // syn
  3647. //case 0x17: // etb
  3648. //case 0x18: // can
  3649. //case 0x1c: // fs
  3650. //case 0x1d: // gs
  3651. //case 0x1e: // rs
  3652. //case 0x1f: // us
  3653. //case '!': // !-
  3654. //case '#': // #-
  3655. //case '&': // &-
  3656. //case '(': // (-
  3657. //case ')': // )-
  3658. //case '*': // *-
  3659. //case '=': // =-
  3660. //case '@': // @-
  3661. //case 'K': // K-
  3662. //case 'Q': // Q-
  3663. //case 'S': // S-
  3664. //case 'V': // V-
  3665. //case '[': // [-
  3666. //case '\\': // \-
  3667. //case ']': // ]-
  3668. //case '_': // _-
  3669. //case '`': // `-
  3670. //case 'v': // v-
  3671. default: // unrecognized command
  3672. buf[0] = c;
  3673. buf[1] = '\0';
  3674. not_implemented(buf);
  3675. end_cmd_q(); // stop adding to q
  3676. case 0x00: // nul- ignore
  3677. break;
  3678. case 2: // ctrl-B scroll up full screen
  3679. case KEYCODE_PAGEUP: // Cursor Key Page Up
  3680. dot_scroll(rows - 2, -1);
  3681. break;
  3682. case 4: // ctrl-D scroll down half screen
  3683. dot_scroll((rows - 2) / 2, 1);
  3684. break;
  3685. case 5: // ctrl-E scroll down one line
  3686. dot_scroll(1, 1);
  3687. break;
  3688. case 6: // ctrl-F scroll down full screen
  3689. case KEYCODE_PAGEDOWN: // Cursor Key Page Down
  3690. dot_scroll(rows - 2, 1);
  3691. break;
  3692. case 7: // ctrl-G show current status
  3693. last_status_cksum = 0; // force status update
  3694. break;
  3695. case 'h': // h- move left
  3696. case KEYCODE_LEFT: // cursor key Left
  3697. case 8: // ctrl-H- move left (This may be ERASE char)
  3698. case 0x7f: // DEL- move left (This may be ERASE char)
  3699. do {
  3700. dot_left();
  3701. } while (--cmdcnt > 0);
  3702. break;
  3703. case 10: // Newline ^J
  3704. case 'j': // j- goto next line, same col
  3705. case KEYCODE_DOWN: // cursor key Down
  3706. case 13: // Carriage Return ^M
  3707. case '+': // +- goto next line
  3708. q = dot;
  3709. do {
  3710. p = next_line(q);
  3711. if (p == end_line(q)) {
  3712. indicate_error();
  3713. goto dc1;
  3714. }
  3715. q = p;
  3716. } while (--cmdcnt > 0);
  3717. dot = q;
  3718. if (c == 13 || c == '+') {
  3719. dot_skip_over_ws();
  3720. } else {
  3721. // try to stay in saved column
  3722. dot = cindex == C_END ? end_line(dot) : move_to_col(dot, cindex);
  3723. keep_index = TRUE;
  3724. }
  3725. break;
  3726. case 12: // ctrl-L force redraw whole screen
  3727. case 18: // ctrl-R force redraw
  3728. redraw(TRUE); // this will redraw the entire display
  3729. break;
  3730. case 21: // ctrl-U scroll up half screen
  3731. dot_scroll((rows - 2) / 2, -1);
  3732. break;
  3733. case 25: // ctrl-Y scroll up one line
  3734. dot_scroll(1, -1);
  3735. break;
  3736. case 27: // esc
  3737. if (cmd_mode == 0)
  3738. indicate_error();
  3739. cmd_mode = 0; // stop inserting
  3740. undo_queue_commit();
  3741. end_cmd_q();
  3742. last_status_cksum = 0; // force status update
  3743. break;
  3744. case ' ': // move right
  3745. case 'l': // move right
  3746. case KEYCODE_RIGHT: // Cursor Key Right
  3747. do {
  3748. dot_right();
  3749. } while (--cmdcnt > 0);
  3750. break;
  3751. #if ENABLE_FEATURE_VI_YANKMARK
  3752. case '"': // "- name a register to use for Delete/Yank
  3753. c1 = (get_one_char() | 0x20) - 'a'; // | 0x20 is tolower()
  3754. if ((unsigned)c1 <= 25) { // a-z?
  3755. YDreg = c1;
  3756. } else {
  3757. indicate_error();
  3758. }
  3759. break;
  3760. case '\'': // '- goto a specific mark
  3761. c1 = (get_one_char() | 0x20);
  3762. if ((unsigned)(c1 - 'a') <= 25) { // a-z?
  3763. c1 = (c1 - 'a');
  3764. // get the b-o-l
  3765. q = mark[c1];
  3766. if (text <= q && q < end) {
  3767. dot = q;
  3768. dot_begin(); // go to B-o-l
  3769. dot_skip_over_ws();
  3770. } else {
  3771. indicate_error();
  3772. }
  3773. } else if (c1 == '\'') { // goto previous context
  3774. dot = swap_context(dot); // swap current and previous context
  3775. dot_begin(); // go to B-o-l
  3776. dot_skip_over_ws();
  3777. #if ENABLE_FEATURE_VI_YANKMARK
  3778. orig_dot = dot; // this doesn't update stored contexts
  3779. #endif
  3780. } else {
  3781. indicate_error();
  3782. }
  3783. break;
  3784. case 'm': // m- Mark a line
  3785. // this is really stupid. If there are any inserts or deletes
  3786. // between text[0] and dot then this mark will not point to the
  3787. // correct location! It could be off by many lines!
  3788. // Well..., at least its quick and dirty.
  3789. c1 = (get_one_char() | 0x20) - 'a';
  3790. if ((unsigned)c1 <= 25) { // a-z?
  3791. // remember the line
  3792. mark[c1] = dot;
  3793. } else {
  3794. indicate_error();
  3795. }
  3796. break;
  3797. case 'P': // P- Put register before
  3798. case 'p': // p- put register after
  3799. p = reg[YDreg];
  3800. if (p == NULL) {
  3801. status_line_bold("Nothing in register %c", what_reg());
  3802. break;
  3803. }
  3804. cnt = 0;
  3805. i = cmdcnt ? cmdcnt : 1;
  3806. // are we putting whole lines or strings
  3807. if (regtype[YDreg] == WHOLE) {
  3808. if (c == 'P') {
  3809. dot_begin(); // putting lines- Put above
  3810. }
  3811. else /* if ( c == 'p') */ {
  3812. // are we putting after very last line?
  3813. if (end_line(dot) == (end - 1)) {
  3814. dot = end; // force dot to end of text[]
  3815. } else {
  3816. dot_next(); // next line, then put before
  3817. }
  3818. }
  3819. } else {
  3820. if (c == 'p')
  3821. dot_right(); // move to right, can move to NL
  3822. // how far to move cursor if register doesn't have a NL
  3823. if (strchr(p, '\n') == NULL)
  3824. cnt = i * strlen(p) - 1;
  3825. }
  3826. do {
  3827. // dot is adjusted if text[] is reallocated so we don't have to
  3828. string_insert(dot, p, allow_undo); // insert the string
  3829. # if ENABLE_FEATURE_VI_UNDO
  3830. allow_undo = ALLOW_UNDO_CHAIN;
  3831. # endif
  3832. } while (--cmdcnt > 0);
  3833. dot += cnt;
  3834. dot_skip_over_ws();
  3835. # if ENABLE_FEATURE_VI_YANKMARK && ENABLE_FEATURE_VI_VERBOSE_STATUS
  3836. yank_status("Put", p, i);
  3837. # endif
  3838. end_cmd_q(); // stop adding to q
  3839. break;
  3840. case 'U': // U- Undo; replace current line with original version
  3841. if (reg[Ureg] != NULL) {
  3842. p = begin_line(dot);
  3843. q = end_line(dot);
  3844. p = text_hole_delete(p, q, ALLOW_UNDO); // delete cur line
  3845. p += string_insert(p, reg[Ureg], ALLOW_UNDO_CHAIN); // insert orig line
  3846. dot = p;
  3847. dot_skip_over_ws();
  3848. # if ENABLE_FEATURE_VI_YANKMARK && ENABLE_FEATURE_VI_VERBOSE_STATUS
  3849. yank_status("Undo", reg[Ureg], 1);
  3850. # endif
  3851. }
  3852. break;
  3853. #endif /* FEATURE_VI_YANKMARK */
  3854. #if ENABLE_FEATURE_VI_UNDO
  3855. case 'u': // u- undo last operation
  3856. undo_pop();
  3857. break;
  3858. #endif
  3859. case '$': // $- goto end of line
  3860. case KEYCODE_END: // Cursor Key End
  3861. for (;;) {
  3862. dot = end_line(dot);
  3863. if (--cmdcnt <= 0)
  3864. break;
  3865. dot_next();
  3866. }
  3867. cindex = C_END;
  3868. keep_index = TRUE;
  3869. break;
  3870. case '%': // %- find matching char of pair () [] {}
  3871. for (q = dot; q < end && *q != '\n'; q++) {
  3872. if (strchr("()[]{}", *q) != NULL) {
  3873. // we found half of a pair
  3874. p = find_pair(q, *q);
  3875. if (p == NULL) {
  3876. indicate_error();
  3877. } else {
  3878. dot = p;
  3879. }
  3880. break;
  3881. }
  3882. }
  3883. if (*q == '\n')
  3884. indicate_error();
  3885. break;
  3886. case 'f': // f- forward to a user specified char
  3887. case 'F': // F- backward to a user specified char
  3888. case 't': // t- move to char prior to next x
  3889. case 'T': // T- move to char after previous x
  3890. last_search_char = get_one_char(); // get the search char
  3891. last_search_cmd = c;
  3892. // fall through
  3893. case ';': // ;- look at rest of line for last search char
  3894. case ',': // ,- repeat latest search in opposite direction
  3895. dot_to_char(c != ',' ? last_search_cmd : last_search_cmd ^ 0x20);
  3896. break;
  3897. #if ENABLE_FEATURE_VI_DOT_CMD
  3898. case '.': // .- repeat the last modifying command
  3899. // Stuff the last_modifying_cmd back into stdin
  3900. // and let it be re-executed.
  3901. if (lmc_len != 0) {
  3902. if (cmdcnt) // update saved count if current count is non-zero
  3903. dotcnt = cmdcnt;
  3904. last_modifying_cmd[lmc_len] = '\0';
  3905. ioq = ioq_start = xasprintf("%u%s", dotcnt, last_modifying_cmd);
  3906. }
  3907. break;
  3908. #endif
  3909. #if ENABLE_FEATURE_VI_SEARCH
  3910. case 'N': // N- backward search for last pattern
  3911. dir = last_search_pattern[0] == '/' ? BACK : FORWARD;
  3912. goto dc4; // now search for pattern
  3913. case '?': // ?- backward search for a pattern
  3914. case '/': // /- forward search for a pattern
  3915. buf[0] = c;
  3916. buf[1] = '\0';
  3917. q = get_input_line(buf); // get input line- use "status line"
  3918. if (!q[0]) // user changed mind and erased the "/"- do nothing
  3919. break;
  3920. if (!q[1]) { // if no pat re-use old pat
  3921. if (last_search_pattern[0])
  3922. last_search_pattern[0] = c;
  3923. } else { // strlen(q) > 1: new pat- save it and find
  3924. vi_free(last_search_pattern);
  3925. last_search_pattern = vi_strdup(q);
  3926. }
  3927. // fall through
  3928. case 'n': // n- repeat search for last pattern
  3929. // search rest of text[] starting at next char
  3930. // if search fails "dot" is unchanged
  3931. dir = last_search_pattern[0] == '/' ? FORWARD : BACK;
  3932. dc4:
  3933. if (last_search_pattern[1] == '\0') {
  3934. status_line_bold("No previous search");
  3935. break;
  3936. }
  3937. do {
  3938. q = char_search(dot + dir, last_search_pattern + 1,
  3939. (dir << 1) | FULL);
  3940. if (q != NULL) {
  3941. dot = q; // good search, update "dot"
  3942. } else {
  3943. // no pattern found between "dot" and top/bottom of file
  3944. // continue from other end of file
  3945. const char *msg;
  3946. q = char_search(dir == FORWARD ? text : end - 1,
  3947. last_search_pattern + 1, (dir << 1) | FULL);
  3948. if (q != NULL) { // found something
  3949. dot = q; // found new pattern- goto it
  3950. msg = "search hit %s, continuing at %s";
  3951. } else { // pattern is nowhere in file
  3952. cmdcnt = 0; // force exit from loop
  3953. msg = "Pattern not found";
  3954. }
  3955. if (dir == FORWARD)
  3956. status_line_bold(msg, "BOTTOM", "TOP");
  3957. else
  3958. status_line_bold(msg, "TOP", "BOTTOM");
  3959. }
  3960. } while (--cmdcnt > 0);
  3961. break;
  3962. case '{': // {- move backward paragraph
  3963. case '}': // }- move forward paragraph
  3964. dir = c == '}' ? FORWARD : BACK;
  3965. do {
  3966. int skip = TRUE; // initially skip consecutive empty lines
  3967. while (dir == FORWARD ? dot < end - 1 : dot > text) {
  3968. if (*dot == '\n' && dot[dir] == '\n') {
  3969. if (!skip) {
  3970. if (dir == FORWARD)
  3971. ++dot; // move to next blank line
  3972. goto dc2;
  3973. }
  3974. }
  3975. else {
  3976. skip = FALSE;
  3977. }
  3978. dot += dir;
  3979. }
  3980. goto dc6; // end of file
  3981. dc2:
  3982. continue;
  3983. } while (--cmdcnt > 0);
  3984. break;
  3985. #endif /* FEATURE_VI_SEARCH */
  3986. case '0': // 0- goto beginning of line
  3987. case '1': // 1-
  3988. case '2': // 2-
  3989. case '3': // 3-
  3990. case '4': // 4-
  3991. case '5': // 5-
  3992. case '6': // 6-
  3993. case '7': // 7-
  3994. case '8': // 8-
  3995. case '9': // 9-
  3996. if (c == '0' && cmdcnt < 1) {
  3997. dot_begin(); // this was a standalone zero
  3998. } else {
  3999. cmdcnt = cmdcnt * 10 + (c - '0'); // this 0 is part of a number
  4000. }
  4001. break;
  4002. case ':': // :- the colon mode commands
  4003. p = get_input_line(":"); // get input line- use "status line"
  4004. colon(p); // execute the command
  4005. break;
  4006. case '<': // <- Left shift something
  4007. case '>': // >- Right shift something
  4008. cnt = count_lines(text, dot); // remember what line we are on
  4009. if (find_range(&p, &q, c) == -1)
  4010. goto dc6;
  4011. i = count_lines(p, q); // # of lines we are shifting
  4012. for (p = begin_line(p); i > 0; i--, p = next_line(p)) {
  4013. if (c == '<') {
  4014. // shift left- remove tab or tabstop spaces
  4015. if (*p == '\t') {
  4016. // shrink buffer 1 char
  4017. text_hole_delete(p, p, allow_undo);
  4018. } else if (*p == ' ') {
  4019. // we should be calculating columns, not just SPACE
  4020. for (j = 0; *p == ' ' && j < tabstop; j++) {
  4021. text_hole_delete(p, p, allow_undo);
  4022. #if ENABLE_FEATURE_VI_UNDO
  4023. allow_undo = ALLOW_UNDO_CHAIN;
  4024. #endif
  4025. }
  4026. }
  4027. } else if (/* c == '>' && */ p != end_line(p)) {
  4028. // shift right -- add tab or tabstop spaces on non-empty lines
  4029. char_insert(p, '\t', allow_undo);
  4030. }
  4031. #if ENABLE_FEATURE_VI_UNDO
  4032. allow_undo = ALLOW_UNDO_CHAIN;
  4033. #endif
  4034. }
  4035. dot = find_line(cnt); // what line were we on
  4036. dot_skip_over_ws();
  4037. end_cmd_q(); // stop adding to q
  4038. break;
  4039. case 'A': // A- append at e-o-l
  4040. dot_end(); // go to e-o-l
  4041. //**** fall through to ... 'a'
  4042. case 'a': // a- append after current char
  4043. if (*dot != '\n')
  4044. dot++;
  4045. goto dc_i;
  4046. case 'B': // B- back a blank-delimited Word
  4047. case 'E': // E- end of a blank-delimited word
  4048. case 'W': // W- forward a blank-delimited word
  4049. dir = FORWARD;
  4050. if (c == 'B')
  4051. dir = BACK;
  4052. do {
  4053. if (c == 'W' || isspace(dot[dir])) {
  4054. dot = skip_thing(dot, 1, dir, S_TO_WS);
  4055. dot = skip_thing(dot, 2, dir, S_OVER_WS);
  4056. }
  4057. if (c != 'W')
  4058. dot = skip_thing(dot, 1, dir, S_BEFORE_WS);
  4059. } while (--cmdcnt > 0);
  4060. break;
  4061. case 'C': // C- Change to e-o-l
  4062. case 'D': // D- delete to e-o-l
  4063. save_dot = dot;
  4064. dot = dollar_line(dot); // move to before NL
  4065. // copy text into a register and delete
  4066. dot = yank_delete(save_dot, dot, PARTIAL, YANKDEL, ALLOW_UNDO); // delete to e-o-l
  4067. if (c == 'C')
  4068. goto dc_i; // start inserting
  4069. #if ENABLE_FEATURE_VI_DOT_CMD
  4070. if (c == 'D')
  4071. end_cmd_q(); // stop adding to q
  4072. #endif
  4073. break;
  4074. case 'g': // 'gg' goto a line number (vim) (default: very first line)
  4075. c1 = get_one_char();
  4076. if (c1 != 'g') {
  4077. buf[0] = 'g';
  4078. // c1 < 0 if the key was special. Try "g<up-arrow>"
  4079. // TODO: if Unicode?
  4080. buf[1] = (c1 >= 0 ? c1 : '*');
  4081. buf[2] = '\0';
  4082. not_implemented(buf);
  4083. cmd_error = TRUE;
  4084. break;
  4085. }
  4086. if (cmdcnt == 0)
  4087. cmdcnt = 1;
  4088. // fall through
  4089. case 'G': // G- goto to a line number (default= E-O-F)
  4090. dot = end - 1; // assume E-O-F
  4091. if (cmdcnt > 0) {
  4092. dot = find_line(cmdcnt); // what line is #cmdcnt
  4093. }
  4094. dot_begin();
  4095. dot_skip_over_ws();
  4096. break;
  4097. case 'H': // H- goto top line on screen
  4098. dot = screenbegin;
  4099. if (cmdcnt > (rows - 1)) {
  4100. cmdcnt = (rows - 1);
  4101. }
  4102. while (--cmdcnt > 0) {
  4103. dot_next();
  4104. }
  4105. dot_begin();
  4106. dot_skip_over_ws();
  4107. break;
  4108. case 'I': // I- insert before first non-blank
  4109. dot_begin(); // 0
  4110. dot_skip_over_ws();
  4111. //**** fall through to ... 'i'
  4112. case 'i': // i- insert before current char
  4113. case KEYCODE_INSERT: // Cursor Key Insert
  4114. dc_i:
  4115. cmd_mode = 1; // start inserting
  4116. undo_queue_commit(); // commit queue when cmd_mode changes
  4117. break;
  4118. case 'J': // J- join current and next lines together
  4119. do {
  4120. dot_end(); // move to NL
  4121. if (dot < end - 1) { // make sure not last char in text[]
  4122. #if ENABLE_FEATURE_VI_UNDO
  4123. undo_push(dot, 1, UNDO_DEL);
  4124. *dot++ = ' '; // replace NL with space
  4125. undo_push((dot - 1), 1, UNDO_INS_CHAIN);
  4126. #else
  4127. *dot++ = ' ';
  4128. modified_count++;
  4129. #endif
  4130. while (isblank(*dot)) { // delete leading WS
  4131. text_hole_delete(dot, dot, ALLOW_UNDO_CHAIN);
  4132. }
  4133. }
  4134. } while (--cmdcnt > 0);
  4135. end_cmd_q(); // stop adding to q
  4136. break;
  4137. case 'L': // L- goto bottom line on screen
  4138. dot = end_screen();
  4139. if (cmdcnt > (rows - 1)) {
  4140. cmdcnt = (rows - 1);
  4141. }
  4142. while (--cmdcnt > 0) {
  4143. dot_prev();
  4144. }
  4145. dot_begin();
  4146. dot_skip_over_ws();
  4147. break;
  4148. case 'M': // M- goto middle line on screen
  4149. dot = screenbegin;
  4150. for (cnt = 0; cnt < (rows-1) / 2; cnt++)
  4151. dot = next_line(dot);
  4152. dot_skip_over_ws();
  4153. break;
  4154. case 'O': // O- open an empty line above
  4155. dot_begin();
  4156. #if ENABLE_FEATURE_VI_SETOPTS
  4157. indentcol = -1;
  4158. #endif
  4159. goto dc3;
  4160. case 'o': // o- open an empty line below
  4161. dot_end();
  4162. dc3:
  4163. dot = char_insert(dot, '\n', ALLOW_UNDO);
  4164. if (c == 'O' && !autoindent) {
  4165. // done in char_insert() for 'O'+autoindent
  4166. dot_prev();
  4167. }
  4168. goto dc_i;
  4169. case 'R': // R- continuous Replace char
  4170. dc5:
  4171. cmd_mode = 2;
  4172. undo_queue_commit();
  4173. break;
  4174. case KEYCODE_DELETE:
  4175. if (dot < end - 1)
  4176. dot = yank_delete(dot, dot, PARTIAL, YANKDEL, ALLOW_UNDO);
  4177. break;
  4178. case 'X': // X- delete char before dot
  4179. case 'x': // x- delete the current char
  4180. case 's': // s- substitute the current char
  4181. dir = 0;
  4182. if (c == 'X')
  4183. dir = -1;
  4184. do {
  4185. if (dot[dir] != '\n') {
  4186. if (c == 'X')
  4187. dot--; // delete prev char
  4188. dot = yank_delete(dot, dot, PARTIAL, YANKDEL, allow_undo); // delete char
  4189. #if ENABLE_FEATURE_VI_UNDO
  4190. allow_undo = ALLOW_UNDO_CHAIN;
  4191. #endif
  4192. }
  4193. } while (--cmdcnt > 0);
  4194. end_cmd_q(); // stop adding to q
  4195. if (c == 's')
  4196. goto dc_i; // start inserting
  4197. break;
  4198. case 'Z': // Z- if modified, {write}; exit
  4199. // ZZ means to save file (if necessary), then exit
  4200. c1 = get_one_char();
  4201. if (c1 != 'Z') {
  4202. indicate_error();
  4203. break;
  4204. }
  4205. if (modified_count) {
  4206. if (ENABLE_FEATURE_VI_READONLY && readonly_mode && current_filename) {
  4207. status_line_bold("'%s' is read only", current_filename);
  4208. break;
  4209. }
  4210. cnt = file_write(current_filename, text, end - 1);
  4211. if (cnt < 0) {
  4212. if (cnt == -1)
  4213. status_line_bold("Write error: %s", strerror(errno));
  4214. } else if (cnt == (end - 1 - text + 1)) {
  4215. editing = 0;
  4216. }
  4217. } else {
  4218. editing = 0;
  4219. }
  4220. // are there other files to edit?
  4221. j = cmdline_filecnt - options.optind - 1;
  4222. if (editing == 0 && j > 0) {
  4223. editing = 1;
  4224. modified_count = 0;
  4225. last_modified_count = -1;
  4226. status_line_bold("%u more file(s) to edit", j);
  4227. }
  4228. break;
  4229. case '^': // ^- move to first non-blank on line
  4230. dot_begin();
  4231. dot_skip_over_ws();
  4232. break;
  4233. case 'b': // b- back a word
  4234. case 'e': // e- end of word
  4235. dir = FORWARD;
  4236. if (c == 'b')
  4237. dir = BACK;
  4238. do {
  4239. if ((dot + dir) < text || (dot + dir) > end - 1)
  4240. break;
  4241. dot += dir;
  4242. if (isspace(*dot)) {
  4243. dot = skip_thing(dot, (c == 'e') ? 2 : 1, dir, S_OVER_WS);
  4244. }
  4245. if (isalnum(*dot) || *dot == '_') {
  4246. dot = skip_thing(dot, 1, dir, S_END_ALNUM);
  4247. } else if (ispunct(*dot)) {
  4248. dot = skip_thing(dot, 1, dir, S_END_PUNCT);
  4249. }
  4250. } while (--cmdcnt > 0);
  4251. break;
  4252. case 'c': // c- change something
  4253. case 'd': // d- delete something
  4254. #if ENABLE_FEATURE_VI_YANKMARK
  4255. case 'y': // y- yank something
  4256. case 'Y': // Y- Yank a line
  4257. #endif
  4258. {
  4259. int yf = YANKDEL; // assume either "c" or "d"
  4260. int buftype;
  4261. #if ENABLE_FEATURE_VI_YANKMARK
  4262. # if ENABLE_FEATURE_VI_VERBOSE_STATUS
  4263. char *savereg = reg[YDreg];
  4264. # endif
  4265. if (c == 'y' || c == 'Y')
  4266. yf = YANKONLY;
  4267. #endif
  4268. // determine range, and whether it spans lines
  4269. buftype = find_range(&p, &q, c);
  4270. if (buftype == -1) // invalid range
  4271. goto dc6;
  4272. if (buftype == WHOLE) {
  4273. save_dot = p; // final cursor position is start of range
  4274. p = begin_line(p);
  4275. q = end_line(q);
  4276. }
  4277. dot = yank_delete(p, q, buftype, yf, ALLOW_UNDO); // delete word
  4278. if (buftype == WHOLE) {
  4279. if (c == 'c') {
  4280. dot = char_insert(dot, '\n', ALLOW_UNDO_CHAIN);
  4281. // on the last line of file don't move to prev line
  4282. if (dot != (end-1)) {
  4283. dot_prev();
  4284. }
  4285. } else if (c == 'd') {
  4286. dot_begin();
  4287. dot_skip_over_ws();
  4288. } else {
  4289. dot = save_dot;
  4290. }
  4291. }
  4292. // if CHANGING, not deleting, start inserting after the delete
  4293. if (c == 'c') {
  4294. goto dc_i; // start inserting
  4295. }
  4296. #if ENABLE_FEATURE_VI_YANKMARK && ENABLE_FEATURE_VI_VERBOSE_STATUS
  4297. // only update status if a yank has actually happened
  4298. if (reg[YDreg] != savereg)
  4299. yank_status(c == 'd' ? "Delete" : "Yank", reg[YDreg], 1);
  4300. #endif
  4301. dc6:
  4302. end_cmd_q(); // stop adding to q
  4303. break;
  4304. }
  4305. case 'k': // k- goto prev line, same col
  4306. case KEYCODE_UP: // cursor key Up
  4307. case '-': // -- goto prev line
  4308. q = dot;
  4309. do {
  4310. p = prev_line(q);
  4311. if (p == begin_line(q)) {
  4312. indicate_error();
  4313. goto dc1;
  4314. }
  4315. q = p;
  4316. } while (--cmdcnt > 0);
  4317. dot = q;
  4318. if (c == '-') {
  4319. dot_skip_over_ws();
  4320. } else {
  4321. // try to stay in saved column
  4322. dot = cindex == C_END ? end_line(dot) : move_to_col(dot, cindex);
  4323. keep_index = TRUE;
  4324. }
  4325. break;
  4326. case 'r': // r- replace the current char with user input
  4327. c1 = get_one_char(); // get the replacement char
  4328. if (c1 != 27) {
  4329. if (end_line(dot) - dot < (cmdcnt ? cmdcnt : 1)) {
  4330. indicate_error();
  4331. goto dc6;
  4332. }
  4333. do {
  4334. dot = text_hole_delete(dot, dot, allow_undo);
  4335. #if ENABLE_FEATURE_VI_UNDO
  4336. allow_undo = ALLOW_UNDO_CHAIN;
  4337. #endif
  4338. dot = char_insert(dot, c1, allow_undo);
  4339. } while (--cmdcnt > 0);
  4340. dot_left();
  4341. }
  4342. end_cmd_q(); // stop adding to q
  4343. break;
  4344. case 'w': // w- forward a word
  4345. do {
  4346. if (isalnum(*dot) || *dot == '_') { // we are on ALNUM
  4347. dot = skip_thing(dot, 1, FORWARD, S_END_ALNUM);
  4348. } else if (ispunct(*dot)) { // we are on PUNCT
  4349. dot = skip_thing(dot, 1, FORWARD, S_END_PUNCT);
  4350. }
  4351. if (dot < end - 1)
  4352. dot++; // move over word
  4353. if (isspace(*dot)) {
  4354. dot = skip_thing(dot, 2, FORWARD, S_OVER_WS);
  4355. }
  4356. } while (--cmdcnt > 0);
  4357. break;
  4358. case 'z': // z-
  4359. c1 = get_one_char(); // get the replacement char
  4360. cnt = 0;
  4361. if (c1 == '.')
  4362. cnt = (rows - 2) / 2; // put dot at center
  4363. if (c1 == '-')
  4364. cnt = rows - 2; // put dot at bottom
  4365. screenbegin = begin_line(dot); // start dot at top
  4366. dot_scroll(cnt, -1);
  4367. break;
  4368. case '|': // |- move to column "cmdcnt"
  4369. dot = move_to_col(dot, cmdcnt - 1); // try to move to column
  4370. break;
  4371. case '~': // ~- flip the case of letters a-z -> A-Z
  4372. do {
  4373. #if ENABLE_FEATURE_VI_UNDO
  4374. if (isalpha(*dot)) {
  4375. undo_push(dot, 1, undo_del);
  4376. *dot = islower(*dot) ? toupper(*dot) : tolower(*dot);
  4377. undo_push(dot, 1, UNDO_INS_CHAIN);
  4378. undo_del = UNDO_DEL_CHAIN;
  4379. }
  4380. #else
  4381. if (islower(*dot)) {
  4382. *dot = toupper(*dot);
  4383. modified_count++;
  4384. } else if (isupper(*dot)) {
  4385. *dot = tolower(*dot);
  4386. modified_count++;
  4387. }
  4388. #endif
  4389. dot_right();
  4390. } while (--cmdcnt > 0);
  4391. end_cmd_q(); // stop adding to q
  4392. break;
  4393. //----- The Cursor and Function Keys -----------------------------
  4394. case KEYCODE_HOME: // Cursor Key Home
  4395. dot_begin();
  4396. break;
  4397. // The Fn keys could point to do_macro which could translate them
  4398. #if 0
  4399. case KEYCODE_FUN1: // Function Key F1
  4400. case KEYCODE_FUN2: // Function Key F2
  4401. case KEYCODE_FUN3: // Function Key F3
  4402. case KEYCODE_FUN4: // Function Key F4
  4403. case KEYCODE_FUN5: // Function Key F5
  4404. case KEYCODE_FUN6: // Function Key F6
  4405. case KEYCODE_FUN7: // Function Key F7
  4406. case KEYCODE_FUN8: // Function Key F8
  4407. case KEYCODE_FUN9: // Function Key F9
  4408. case KEYCODE_FUN10: // Function Key F10
  4409. case KEYCODE_FUN11: // Function Key F11
  4410. case KEYCODE_FUN12: // Function Key F12
  4411. break;
  4412. #endif
  4413. }
  4414. dc1:
  4415. // if text[] just became empty, add back an empty line
  4416. if (end == text) {
  4417. char_insert(text, '\n', NO_UNDO); // start empty buf with dummy line
  4418. dot = text;
  4419. }
  4420. // it is OK for dot to exactly equal to end, otherwise check dot validity
  4421. if (dot != end) {
  4422. dot = bound_dot(dot); // make sure "dot" is valid
  4423. }
  4424. #if ENABLE_FEATURE_VI_YANKMARK
  4425. if (dot != orig_dot)
  4426. check_context(c); // update the current context
  4427. #endif
  4428. if (!isdigit(c))
  4429. cmdcnt = 0; // cmd was not a number, reset cmdcnt
  4430. cnt = dot - begin_line(dot);
  4431. // Try to stay off of the Newline
  4432. if (*dot == '\n' && cnt > 0 && cmd_mode == 0)
  4433. dot--;
  4434. }
  4435. /* NB! the CRASHME code is unmaintained, and doesn't currently build */
  4436. #if ENABLE_FEATURE_VI_CRASHME
  4437. static int totalcmds = 0;
  4438. static int Mp = 85; // Movement command Probability
  4439. static int Np = 90; // Non-movement command Probability
  4440. static int Dp = 96; // Delete command Probability
  4441. static int Ip = 97; // Insert command Probability
  4442. static int Yp = 98; // Yank command Probability
  4443. static int Pp = 99; // Put command Probability
  4444. static int M = 0, N = 0, I = 0, D = 0, Y = 0, P = 0, U = 0;
  4445. static const char chars[20] = "\t012345 abcdABCD-=.$";
  4446. static const char *const words[20] = {
  4447. "this", "is", "a", "test",
  4448. "broadcast", "the", "emergency", "of",
  4449. "system", "quick", "brown", "fox",
  4450. "jumped", "over", "lazy", "dogs",
  4451. "back", "January", "Febuary", "March"
  4452. };
  4453. static const char *const lines[20] = {
  4454. "You should have received a copy of the GNU General Public License\n",
  4455. "char c, cm, *cmd, *cmd1;\n",
  4456. "generate a command by percentages\n",
  4457. "Numbers may be typed as a prefix to some commands.\n",
  4458. "Quit, discarding changes!\n",
  4459. "Forced write, if permission originally not valid.\n",
  4460. "In general, any ex or ed command (such as substitute or delete).\n",
  4461. "I have tickets available for the Blazers vs LA Clippers for Monday, Janurary 1 at 1:00pm.\n",
  4462. "Please get w/ me and I will go over it with you.\n",
  4463. "The following is a list of scheduled, committed changes.\n",
  4464. "1. Launch Norton Antivirus (Start, Programs, Norton Antivirus)\n",
  4465. "Reminder....Town Meeting in Central Perk cafe today at 3:00pm.\n",
  4466. "Any question about transactions please contact Sterling Huxley.\n",
  4467. "I will try to get back to you by Friday, December 31.\n",
  4468. "This Change will be implemented on Friday.\n",
  4469. "Let me know if you have problems accessing this;\n",
  4470. "Sterling Huxley recently added you to the access list.\n",
  4471. "Would you like to go to lunch?\n",
  4472. "The last command will be automatically run.\n",
  4473. "This is too much english for a computer geek.\n",
  4474. };
  4475. static char *multilines[20] = {
  4476. "You should have received a copy of the GNU General Public License\n",
  4477. "char c, cm, *cmd, *cmd1;\n",
  4478. "generate a command by percentages\n",
  4479. "Numbers may be typed as a prefix to some commands.\n",
  4480. "Quit, discarding changes!\n",
  4481. "Forced write, if permission originally not valid.\n",
  4482. "In general, any ex or ed command (such as substitute or delete).\n",
  4483. "I have tickets available for the Blazers vs LA Clippers for Monday, Janurary 1 at 1:00pm.\n",
  4484. "Please get w/ me and I will go over it with you.\n",
  4485. "The following is a list of scheduled, committed changes.\n",
  4486. "1. Launch Norton Antivirus (Start, Programs, Norton Antivirus)\n",
  4487. "Reminder....Town Meeting in Central Perk cafe today at 3:00pm.\n",
  4488. "Any question about transactions please contact Sterling Huxley.\n",
  4489. "I will try to get back to you by Friday, December 31.\n",
  4490. "This Change will be implemented on Friday.\n",
  4491. "Let me know if you have problems accessing this;\n",
  4492. "Sterling Huxley recently added you to the access list.\n",
  4493. "Would you like to go to lunch?\n",
  4494. "The last command will be automatically run.\n",
  4495. "This is too much english for a computer geek.\n",
  4496. };
  4497. // create a random command to execute
  4498. static void crash_dummy()
  4499. {
  4500. static int sleeptime; // how long to pause between commands
  4501. char c, cm, *cmd, *cmd1;
  4502. int i, cnt, thing, rbi, startrbi, percent;
  4503. // "dot" movement commands
  4504. cmd1 = " \n\r\002\004\005\006\025\0310^$-+wWeEbBhjklHL";
  4505. // is there already a command running?
  4506. if (readbuffer[0] > 0)
  4507. goto cd1;
  4508. cd0:
  4509. readbuffer[0] = 'X';
  4510. startrbi = rbi = 1;
  4511. sleeptime = 0; // how long to pause between commands
  4512. rt_memset(readbuffer, '\0', sizeof(readbuffer));
  4513. // generate a command by percentages
  4514. percent = (int) lrand48() % 100; // get a number from 0-99
  4515. if (percent < Mp) { // Movement commands
  4516. // available commands
  4517. cmd = cmd1;
  4518. M++;
  4519. } else if (percent < Np) { // non-movement commands
  4520. cmd = "mz<>\'\""; // available commands
  4521. N++;
  4522. } else if (percent < Dp) { // Delete commands
  4523. cmd = "dx"; // available commands
  4524. D++;
  4525. } else if (percent < Ip) { // Inset commands
  4526. cmd = "iIaAsrJ"; // available commands
  4527. I++;
  4528. } else if (percent < Yp) { // Yank commands
  4529. cmd = "yY"; // available commands
  4530. Y++;
  4531. } else if (percent < Pp) { // Put commands
  4532. cmd = "pP"; // available commands
  4533. P++;
  4534. } else {
  4535. // We do not know how to handle this command, try again
  4536. U++;
  4537. goto cd0;
  4538. }
  4539. // randomly pick one of the available cmds from "cmd[]"
  4540. i = (int) lrand48() % strlen(cmd);
  4541. cm = cmd[i];
  4542. if (strchr(":\024", cm))
  4543. goto cd0; // dont allow colon or ctrl-T commands
  4544. readbuffer[rbi++] = cm; // put cmd into input buffer
  4545. // now we have the command-
  4546. // there are 1, 2, and multi char commands
  4547. // find out which and generate the rest of command as necessary
  4548. if (strchr("dmryz<>\'\"", cm)) { // 2-char commands
  4549. cmd1 = " \n\r0$^-+wWeEbBhjklHL";
  4550. if (cm == 'm' || cm == '\'' || cm == '\"') { // pick a reg[]
  4551. cmd1 = "abcdefghijklmnopqrstuvwxyz";
  4552. }
  4553. thing = (int) lrand48() % strlen(cmd1); // pick a movement command
  4554. c = cmd1[thing];
  4555. readbuffer[rbi++] = c; // add movement to input buffer
  4556. }
  4557. if (strchr("iIaAsc", cm)) { // multi-char commands
  4558. if (cm == 'c') {
  4559. // change some thing
  4560. thing = (int) lrand48() % strlen(cmd1); // pick a movement command
  4561. c = cmd1[thing];
  4562. readbuffer[rbi++] = c; // add movement to input buffer
  4563. }
  4564. thing = (int) lrand48() % 4; // what thing to insert
  4565. cnt = (int) lrand48() % 10; // how many to insert
  4566. for (i = 0; i < cnt; i++) {
  4567. if (thing == 0) { // insert chars
  4568. readbuffer[rbi++] = chars[((int) lrand48() % strlen(chars))];
  4569. } else if (thing == 1) { // insert words
  4570. strcat(readbuffer, words[(int) lrand48() % 20]);
  4571. strcat(readbuffer, " ");
  4572. sleeptime = 0; // how fast to type
  4573. } else if (thing == 2) { // insert lines
  4574. strcat(readbuffer, lines[(int) lrand48() % 20]);
  4575. sleeptime = 0; // how fast to type
  4576. } else { // insert multi-lines
  4577. strcat(readbuffer, multilines[(int) lrand48() % 20]);
  4578. sleeptime = 0; // how fast to type
  4579. }
  4580. }
  4581. strcat(readbuffer, "\033");
  4582. }
  4583. readbuffer[0] = strlen(readbuffer + 1);
  4584. cd1:
  4585. totalcmds++;
  4586. if (sleeptime > 0)
  4587. mysleep(sleeptime); // sleep 1/100 sec
  4588. }
  4589. // test to see if there are any errors
  4590. static void crash_test()
  4591. {
  4592. static time_t oldtim;
  4593. time_t tim;
  4594. char d[2], msg[80];
  4595. msg[0] = '\0';
  4596. if (end < text) {
  4597. strcat(msg, "end<text ");
  4598. }
  4599. if (end > textend) {
  4600. strcat(msg, "end>textend ");
  4601. }
  4602. if (dot < text) {
  4603. strcat(msg, "dot<text ");
  4604. }
  4605. if (dot > end) {
  4606. strcat(msg, "dot>end ");
  4607. }
  4608. if (screenbegin < text) {
  4609. strcat(msg, "screenbegin<text ");
  4610. }
  4611. if (screenbegin > end - 1) {
  4612. strcat(msg, "screenbegin>end-1 ");
  4613. }
  4614. if (msg[0]) {
  4615. rt_kprintf("\n\n%d: \'%c\' %s\n\n\n%s[Hit return to continue]%s",
  4616. totalcmds, last_input_char, msg, ESC_BOLD_TEXT, ESC_NORM_TEXT);
  4617. fflush_all();
  4618. while (safe_read(STDIN_FILENO, d, 1) > 0) {
  4619. if (d[0] == '\n' || d[0] == '\r')
  4620. break;
  4621. }
  4622. }
  4623. tim = time(NULL);
  4624. if (tim >= (oldtim + 3)) {
  4625. sprintf(status_buffer,
  4626. "Tot=%d: M=%d N=%d I=%d D=%d Y=%d P=%d U=%d size=%d",
  4627. totalcmds, M, N, I, D, Y, P, U, end - text + 1);
  4628. oldtim = tim;
  4629. }
  4630. }
  4631. #endif