vi.c 137 KB

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