3
0

vi.c 107 KB

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