3
0

vi.c 112 KB

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