vi.c 142 KB

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