vi.c 106 KB

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