vi.c 110 KB

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