exec.b 27 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350
  1. implement Exec;
  2. include "common.m";
  3. sys : Sys;
  4. dat : Dat;
  5. acme : Acme;
  6. utils : Utils;
  7. graph : Graph;
  8. gui : Gui;
  9. lookx : Look;
  10. bufferm : Bufferm;
  11. textm : Textm;
  12. scrl : Scroll;
  13. filem : Filem;
  14. windowm : Windowm;
  15. rowm : Rowm;
  16. columnm : Columnm;
  17. fsys : Fsys;
  18. editm: Edit;
  19. Dir, OREAD, OWRITE : import Sys;
  20. EVENTSIZE, QWaddr, QWdata, QWevent, Astring : import dat;
  21. Lock, Reffont, Ref, seltext, seq, row : import dat;
  22. warning, error, skipbl, findbl, stralloc, strfree, exec : import utils;
  23. dirname : import lookx;
  24. Body, Text : import textm;
  25. File : import filem;
  26. sprint : import sys;
  27. TRUE, FALSE, XXX, BUFSIZE : import Dat;
  28. Buffer : import bufferm;
  29. Row : import rowm;
  30. Column : import columnm;
  31. Window : import windowm;
  32. setalphabet: import textm;
  33. # snarfbuf : ref Buffer;
  34. init(mods : ref Dat->Mods)
  35. {
  36. sys = mods.sys;
  37. dat = mods.dat;
  38. acme = mods.acme;
  39. utils = mods.utils;
  40. graph = mods.graph;
  41. gui = mods.gui;
  42. lookx = mods.look;
  43. bufferm = mods.bufferm;
  44. textm = mods.textm;
  45. scrl = mods.scroll;
  46. filem = mods.filem;
  47. rowm = mods.rowm;
  48. windowm = mods.windowm;
  49. columnm = mods.columnm;
  50. fsys = mods.fsys;
  51. editm = mods.edit;
  52. snarfbuf = bufferm->newbuffer();
  53. }
  54. Exectab : adt {
  55. name : string;
  56. fun : int;
  57. mark : int;
  58. flag1 : int;
  59. flag2 : int;
  60. };
  61. F_ALPHABET, F_CUT, F_DEL, F_DELCOL, F_DUMP, F_EDIT, F_EXITX, F_FONTX, F_GET, F_ID, F_INCL, F_KILL, F_LIMBO, F_LINENO, F_LOCAL, F_LOOK, F_NEW, F_NEWCOL, F_PASTE, F_PUT, F_PUTALL, F_UNDO, F_SEND, F_SORT, F_TAB, F_ZEROX : con iota;
  62. exectab := array[] of {
  63. Exectab ( "Alphabet", F_ALPHABET, FALSE, XXX, XXX ),
  64. Exectab ( "Cut", F_CUT, TRUE, TRUE, TRUE ),
  65. Exectab ( "Del", F_DEL, FALSE, FALSE, XXX ),
  66. Exectab ( "Delcol", F_DELCOL, FALSE, XXX, XXX ),
  67. Exectab ( "Delete", F_DEL, FALSE, TRUE, XXX ),
  68. Exectab ( "Dump", F_DUMP, FALSE, TRUE, XXX ),
  69. Exectab ( "Edit", F_EDIT, FALSE, XXX, XXX ),
  70. Exectab ( "Exit", F_EXITX, FALSE, XXX, XXX ),
  71. Exectab ( "Font", F_FONTX, FALSE, XXX, XXX ),
  72. Exectab ( "Get", F_GET, FALSE, TRUE, XXX ),
  73. Exectab ( "ID", F_ID, FALSE, XXX, XXX ),
  74. Exectab ( "Incl", F_INCL, FALSE, XXX, XXX ),
  75. Exectab ( "Kill", F_KILL, FALSE, XXX, XXX ),
  76. Exectab ( "Limbo", F_LIMBO, FALSE, XXX, XXX ),
  77. Exectab ( "Lineno", F_LINENO, FALSE, XXX, XXX ),
  78. Exectab ( "Load", F_DUMP, FALSE, FALSE, XXX ),
  79. Exectab ( "Local", F_LOCAL, FALSE, XXX, XXX ),
  80. Exectab ( "Look", F_LOOK, FALSE, XXX, XXX ),
  81. Exectab ( "New", F_NEW, FALSE, XXX, XXX ),
  82. Exectab ( "Newcol", F_NEWCOL, FALSE, XXX, XXX ),
  83. Exectab ( "Paste", F_PASTE, TRUE, TRUE, XXX ),
  84. Exectab ( "Put", F_PUT, FALSE, XXX, XXX ),
  85. Exectab ( "Putall", F_PUTALL, FALSE, XXX, XXX ),
  86. Exectab ( "Redo", F_UNDO, FALSE, FALSE, XXX ),
  87. Exectab ( "Send", F_SEND, TRUE, XXX, XXX ),
  88. Exectab ( "Snarf", F_CUT, FALSE, TRUE, FALSE ),
  89. Exectab ( "Sort", F_SORT, FALSE, XXX, XXX ),
  90. Exectab ( "Tab", F_TAB, FALSE, XXX, XXX ),
  91. Exectab ( "Undo", F_UNDO, FALSE, TRUE, XXX ),
  92. Exectab ( "Zerox", F_ZEROX, FALSE, XXX, XXX ),
  93. Exectab ( nil, 0, 0, 0, 0 ),
  94. };
  95. runfun(fun : int, et, t, argt : ref Text, flag1, flag2 : int, arg : string, narg : int)
  96. {
  97. case (fun) {
  98. F_ALPHABET => alphabet(et, argt, arg, narg);
  99. F_CUT => cut(et, t, flag1, flag2);
  100. F_DEL => del(et, flag1);
  101. F_DELCOL => delcol(et);
  102. F_DUMP => dump(argt, flag1, arg, narg);
  103. F_EDIT => edit(et, argt, arg, narg);
  104. F_EXITX => exitx();
  105. F_FONTX => fontx(et, t, argt, arg, narg);
  106. F_GET => get(et, t, argt, flag1, arg, narg);
  107. F_ID => id(et);
  108. F_INCL => incl(et, argt, arg, narg);
  109. F_KILL => kill(argt, arg, narg);
  110. F_LIMBO => limbo(et);
  111. F_LINENO => lineno(et);
  112. F_LOCAL => local(et, argt, arg);
  113. F_LOOK => look(et, t, argt);
  114. F_NEW => lookx->new(et, t, argt, flag1, flag2, arg, narg);
  115. F_NEWCOL => newcol(et);
  116. F_PASTE => paste(et, t, flag1, flag2);
  117. F_PUT => put(et, argt, arg, narg);
  118. F_PUTALL => putall();
  119. F_UNDO => undo(et, flag1);
  120. F_SEND => send(et, t);
  121. F_SORT => sort(et);
  122. F_TAB => tab(et, argt, arg, narg);
  123. F_ZEROX => zerox(et, t);
  124. * => error("bad case in runfun()");
  125. }
  126. }
  127. lookup(r : string, n : int) : int
  128. {
  129. nr : int;
  130. (r, n) = skipbl(r, n);
  131. if(n == 0)
  132. return -1;
  133. (nil, nr) = findbl(r, n);
  134. nr = n-nr;
  135. for(i := 0; exectab[i].name != nil; i++)
  136. if (r[0:nr] == exectab[i].name)
  137. return i;
  138. return -1;
  139. }
  140. isexecc(c : int) : int
  141. {
  142. if(lookx->isfilec(c))
  143. return 1;
  144. return c=='<' || c=='|' || c=='>';
  145. }
  146. execute(t : ref Text, aq0 : int, aq1 : int, external : int, argt : ref Text)
  147. {
  148. q0, q1 : int;
  149. r : ref Astring;
  150. s, dir, aa, a : string;
  151. e : int;
  152. c, n, f : int;
  153. q0 = aq0;
  154. q1 = aq1;
  155. if(q1 == q0){ # expand to find word (actually file name)
  156. # if in selection, choose selection
  157. if(t.q1>t.q0 && t.q0<=q0 && q0<=t.q1){
  158. q0 = t.q0;
  159. q1 = t.q1;
  160. }else{
  161. while(q1<t.file.buf.nc && isexecc(c=t.readc(q1)) && c!=':')
  162. q1++;
  163. while(q0>0 && isexecc(c=t.readc(q0-1)) && c!=':')
  164. q0--;
  165. if(q1 == q0)
  166. return;
  167. }
  168. }
  169. r = stralloc(q1-q0);
  170. t.file.buf.read(q0, r, 0, q1-q0);
  171. e = lookup(r.s, q1-q0);
  172. if(!external && t.w!=nil && t.w.nopen[QWevent]>byte 0){
  173. f = 0;
  174. if(e >= 0)
  175. f |= 1;
  176. if(q0!=aq0 || q1!=aq1){
  177. t.file.buf.read(aq0, r, 0, aq1-aq0);
  178. f |= 2;
  179. }
  180. (aa, a) = getbytearg(argt, TRUE, TRUE);
  181. if(a != nil){
  182. if(len a > EVENTSIZE){ # too big; too bad
  183. aa = a = nil;
  184. warning(nil, "`argument string too long\n");
  185. return;
  186. }
  187. f |= 8;
  188. }
  189. c = 'x';
  190. if(t.what == Body)
  191. c = 'X';
  192. n = aq1-aq0;
  193. if(n <= EVENTSIZE)
  194. t.w.event(sprint("%c%d %d %d %d %s\n", c, aq0, aq1, f, n, r.s[0:n]));
  195. else
  196. t.w.event(sprint("%c%d %d %d 0 \n", c, aq0, aq1, f));
  197. if(q0!=aq0 || q1!=aq1){
  198. n = q1-q0;
  199. t.file.buf.read(q0, r, 0, n);
  200. if(n <= EVENTSIZE)
  201. t.w.event(sprint("%c%d %d 0 %d %s\n", c, q0, q1, n, r.s[0:n]));
  202. else
  203. t.w.event(sprint("%c%d %d 0 0 \n", c, q0, q1));
  204. }
  205. if(a != nil){
  206. t.w.event(sprint("%c0 0 0 %d %s\n", c, len a, a));
  207. if(aa != nil)
  208. t.w.event(sprint("%c0 0 0 %d %s\n", c, len aa, aa));
  209. else
  210. t.w.event(sprint("%c0 0 0 0 \n", c));
  211. }
  212. strfree(r);
  213. r = nil;
  214. a = aa = nil;
  215. return;
  216. }
  217. if(e >= 0){
  218. if(exectab[e].mark && seltext!=nil)
  219. if(seltext.what == Body){
  220. seq++;
  221. seltext.w.body.file.mark();
  222. }
  223. (s, n) = skipbl(r.s, q1-q0);
  224. (s, n) = findbl(s, n);
  225. (s, n) = skipbl(s, n);
  226. runfun(exectab[e].fun, t, seltext, argt, exectab[e].flag1, exectab[e].flag2, s, n);
  227. strfree(r);
  228. r = nil;
  229. return;
  230. }
  231. (dir, n) = dirname(t, nil, 0);
  232. if(n==1 && dir[0]=='.'){ # sigh
  233. dir = nil;
  234. n = 0;
  235. }
  236. (aa, a) = getbytearg(argt, TRUE, TRUE);
  237. if(t.w != nil)
  238. t.w.refx.inc();
  239. spawn run(t.w, r.s, dir, n, TRUE, aa, a, FALSE);
  240. }
  241. printarg(argt : ref Text, q0 : int, q1 : int) : string
  242. {
  243. buf : string;
  244. if(argt.what!=Body || argt.file.name==nil)
  245. return nil;
  246. if(q0 == q1)
  247. buf = sprint("%s:#%d", argt.file.name, q0);
  248. else
  249. buf = sprint("%s:#%d,#%d", argt.file.name, q0, q1);
  250. return buf;
  251. }
  252. getarg(argt : ref Text, doaddr : int, dofile : int) : (string, string, int)
  253. {
  254. r : ref Astring;
  255. n : int;
  256. e : Dat->Expand;
  257. a : string;
  258. ok : int;
  259. if(argt == nil)
  260. return (nil, nil, 0);
  261. a = nil;
  262. argt.commit(TRUE);
  263. (ok, e) = lookx->expand(argt, argt.q0, argt.q1);
  264. if (ok) {
  265. e.bname = nil;
  266. if(len e.name && dofile){
  267. if(doaddr)
  268. a = printarg(argt, e.q0, e.q1);
  269. return (a, e.name, len e.name);
  270. }
  271. e.name = nil;
  272. }else{
  273. e.q0 = argt.q0;
  274. e.q1 = argt.q1;
  275. }
  276. n = e.q1 - e.q0;
  277. r = stralloc(n);
  278. argt.file.buf.read(e.q0, r, 0, n);
  279. if(doaddr)
  280. a = printarg(argt, e.q0, e.q1);
  281. return(a, r.s, n);
  282. }
  283. getbytearg(argt : ref Text, doaddr : int, dofile : int) : (string, string)
  284. {
  285. r : string;
  286. n : int;
  287. aa : string;
  288. (aa, r, n) = getarg(argt, doaddr, dofile);
  289. if(r == nil)
  290. return (nil, nil);
  291. return (aa, r);
  292. }
  293. newcol(et : ref Text)
  294. {
  295. c : ref Column;
  296. c = et.row.add(nil, -1);
  297. if(c != nil)
  298. c.add(nil, nil, -1).settag();
  299. }
  300. delcol(et : ref Text)
  301. {
  302. c := et.col;
  303. if(c==nil || !c.clean(FALSE))
  304. return;
  305. for(i:=0; i<c.nw; i++){
  306. w := c.w[i];
  307. if(int w.nopen[QWevent]+int w.nopen[QWaddr]+int w.nopen[QWdata] > 0){
  308. warning(nil, sys->sprint("can't delete column; %s is running an external command\n", w.body.file.name));
  309. return;
  310. }
  311. }
  312. c.row.close(c, TRUE);
  313. }
  314. del(et : ref Text, flag1 : int)
  315. {
  316. if(et.col==nil || et.w == nil)
  317. return;
  318. if(flag1 || et.w.body.file.ntext>1 || et.w.clean(FALSE, FALSE))
  319. et.col.close(et.w, TRUE);
  320. }
  321. sort(et : ref Text)
  322. {
  323. if(et.col != nil)
  324. et.col.sort();
  325. }
  326. seqof(w: ref Window, isundo: int): int
  327. {
  328. # if it's undo, see who changed with us
  329. if(isundo)
  330. return w.body.file.seq;
  331. # if it's redo, see who we'll be sync'ed up with
  332. return w.body.file.redoseq();
  333. }
  334. undo(et : ref Text, flag1 : int)
  335. {
  336. i, j: int;
  337. c: ref Column;
  338. w: ref Window;
  339. seq: int;
  340. if(et==nil || et.w== nil)
  341. return;
  342. seq = seqof(et.w, flag1);
  343. for(i=0; i<row.ncol; i++){
  344. c = row.col[i];
  345. for(j=0; j<c.nw; j++){
  346. w = c.w[j];
  347. if(seqof(w, flag1) == seq)
  348. w.undo(flag1);
  349. }
  350. }
  351. # et.w.undo(flag1);
  352. }
  353. getname(t : ref Text, argt : ref Text, arg : string, narg : int, isput : int) : string
  354. {
  355. r, dir : string;
  356. i, n, ndir, promote : int;
  357. (nil, r, n) = getarg(argt, FALSE, TRUE);
  358. promote = FALSE;
  359. if(r == nil)
  360. promote = TRUE;
  361. else if(isput){
  362. # if are doing a Put, want to synthesize name even for non-existent file
  363. # best guess is that file name doesn't contain a slash
  364. promote = TRUE;
  365. for(i=0; i<n; i++)
  366. if(r[i] == '/'){
  367. promote = FALSE;
  368. break;
  369. }
  370. if(promote){
  371. t = argt;
  372. arg = r;
  373. narg = n;
  374. }
  375. }
  376. if(promote){
  377. n = narg;
  378. if(n <= 0)
  379. return t.file.name;
  380. # prefix with directory name if necessary
  381. dir = nil;
  382. ndir = 0;
  383. if(n>0 && arg[0]!='/'){
  384. (dir, ndir) = dirname(t, nil, 0);
  385. if(n==1 && dir[0]=='.'){ # sigh
  386. dir = nil;
  387. ndir = 0;
  388. }
  389. }
  390. if(dir != nil){
  391. r = dir[0:ndir] + arg[0:n];
  392. dir = nil;
  393. n += ndir;
  394. }else
  395. r = arg[0:n];
  396. }
  397. return r;
  398. }
  399. zerox(et : ref Text, t : ref Text)
  400. {
  401. nw : ref Window;
  402. c, locked : int;
  403. locked = FALSE;
  404. if(t!=nil && t.w!=nil && t.w!=et.w){
  405. locked = TRUE;
  406. c = 'M';
  407. if(et.w != nil)
  408. c = et.w.owner;
  409. t.w.lock(c);
  410. }
  411. if(t == nil)
  412. t = et;
  413. if(t==nil || t.w==nil)
  414. return;
  415. t = t.w.body;
  416. if(t.w.isdir)
  417. warning(nil, sprint("%s is a directory; Zerox illegal\n", t.file.name));
  418. else{
  419. nw = t.w.col.add(nil, t.w, -1);
  420. # ugly: fix locks so w.unlock works
  421. nw.lock1(t.w.owner);
  422. }
  423. if(locked)
  424. t.w.unlock();
  425. }
  426. get(et : ref Text, t : ref Text, argt : ref Text, flag1 : int, arg : string, narg : int)
  427. {
  428. name : string;
  429. r : string;
  430. i, n, dirty : int;
  431. w : ref Window;
  432. u : ref Text;
  433. d : Dir;
  434. ok : int;
  435. if(flag1)
  436. if(et==nil || et.w==nil)
  437. return;
  438. if(!et.w.isdir && (et.w.body.file.buf.nc>0 && !et.w.clean(TRUE, FALSE)))
  439. return;
  440. w = et.w;
  441. t = w.body;
  442. name = getname(t, argt, arg, narg, FALSE);
  443. if(name == nil){
  444. warning(nil, "no file name\n");
  445. return;
  446. }
  447. if(t.file.ntext>1){
  448. (ok, d) = sys->stat(name);
  449. if (ok == 0 && d.qid.qtype & Sys->QTDIR) {
  450. warning(nil, sprint("%s is a directory; can't read with multiple windows on it\n", name));
  451. return;
  452. }
  453. }
  454. r = name;
  455. n = len name;
  456. for(i=0; i<t.file.ntext; i++){
  457. u = t.file.text[i];
  458. # second and subsequent calls with zero an already empty buffer, but OK
  459. u.reset();
  460. u.w.dirfree();
  461. }
  462. samename := r[0:n] == t.file.name;
  463. t.loadx(0, name, samename);
  464. if(samename){
  465. t.file.mod = FALSE;
  466. dirty = FALSE;
  467. }else{
  468. t.file.mod = TRUE;
  469. dirty = TRUE;
  470. }
  471. for(i=0; i<t.file.ntext; i++)
  472. t.file.text[i].w.dirty = dirty;
  473. name = nil;
  474. r = nil;
  475. w.settag();
  476. t.file.unread = FALSE;
  477. for(i=0; i<t.file.ntext; i++){
  478. u = t.file.text[i];
  479. u.w.tag.setselect(u.w.tag.file.buf.nc, u.w.tag.file.buf.nc);
  480. scrl->scrdraw(u);
  481. }
  482. }
  483. putfile(f: ref File, q0: int, q1: int, name: string)
  484. {
  485. n : int;
  486. r, s : ref Astring;
  487. w : ref Window;
  488. i, q : int;
  489. fd : ref Sys->FD;
  490. d : Dir;
  491. ok : int;
  492. w = f.curtext.w;
  493. {
  494. if(name == f.name){
  495. (ok, d) = sys->stat(name);
  496. if(ok >= 0 && (f.dev!=d.dev || f.qidpath!=d.qid.path || f.mtime<d.mtime)){
  497. f.dev = d.dev;
  498. f.qidpath = d.qid.path;
  499. f.mtime = d.mtime;
  500. if(f.unread)
  501. warning(nil, sys->sprint("%s not written; file already exists\n", name));
  502. else
  503. warning(nil, sys->sprint("%s modified since last read\n", name));
  504. raise "e";
  505. }
  506. }
  507. fd = sys->create(name, OWRITE, 8r664); # was 666
  508. if(fd == nil){
  509. warning(nil, sprint("can't create file %s: %r\n", name));
  510. raise "e";
  511. }
  512. r = stralloc(BUFSIZE);
  513. s = stralloc(BUFSIZE);
  514. {
  515. (ok, d) = sys->fstat(fd);
  516. if(ok>=0 && (d.mode&Sys->DMAPPEND) && d.length>big 0){
  517. warning(nil, sprint("%s not written; file is append only\n", name));
  518. raise "e";
  519. }
  520. for(q = q0; q < q1; q += n){
  521. n = q1 - q;
  522. if(n > BUFSIZE)
  523. n = BUFSIZE;
  524. f.buf.read(q, r, 0, n);
  525. ab := array of byte r.s[0:n];
  526. if(sys->write(fd, ab, len ab) != len ab){
  527. ab = nil;
  528. warning(nil, sprint("can't write file %s: %r\n", name));
  529. raise "e";
  530. }
  531. ab = nil;
  532. }
  533. if(name == f.name){
  534. d0 : Dir;
  535. if(q0 != 0 || q1 != f.buf.nc){
  536. f.mod = TRUE;
  537. w.dirty = TRUE;
  538. f.unread = TRUE;
  539. }
  540. else{
  541. (ok, d0) = sys->fstat(fd); # use old values if we failed
  542. if (ok >= 0)
  543. d = d0;
  544. f.qidpath = d.qid.path;
  545. f.dev = d.dev;
  546. f.mtime = d.mtime;
  547. f.mod = FALSE;
  548. w.dirty = FALSE;
  549. f.unread = FALSE;
  550. }
  551. for(i=0; i<f.ntext; i++){
  552. f.text[i].w.putseq = f.seq;
  553. f.text[i].w.dirty = w.dirty;
  554. }
  555. }
  556. strfree(s);
  557. strfree(r);
  558. s = r = nil;
  559. name = nil;
  560. fd = nil;
  561. w.settag();
  562. }
  563. exception{
  564. * =>
  565. strfree(s);
  566. strfree(r);
  567. s = r = nil;
  568. fd = nil;
  569. raise "e";
  570. }
  571. }
  572. exception{
  573. * =>
  574. name = nil;
  575. return;
  576. }
  577. }
  578. put(et : ref Text, argt : ref Text, arg : string, narg : int)
  579. {
  580. namer : string;
  581. name : string;
  582. w : ref Window;
  583. if(et==nil || et.w==nil || et.w.isdir)
  584. return;
  585. w = et.w;
  586. f := w.body.file;
  587. name = getname(w.body, argt, arg, narg, TRUE);
  588. if(name == nil){
  589. warning(nil, "no file name\n");
  590. return;
  591. }
  592. namer = name;
  593. putfile(f, 0, f.buf.nc, namer);
  594. name = nil;
  595. }
  596. dump(argt : ref Text, isdump : int, arg : string, narg : int)
  597. {
  598. name : string;
  599. if(narg)
  600. name = arg;
  601. else
  602. (nil, name) = getbytearg(argt, FALSE, TRUE);
  603. if(isdump)
  604. row.dump(name);
  605. else {
  606. if (!row.qlock.locked())
  607. error("row not locked in dump()");
  608. row.loadx(name, FALSE);
  609. }
  610. name = nil;
  611. }
  612. cut(et : ref Text, t : ref Text, dosnarf : int, docut : int)
  613. {
  614. q0, q1, n, locked, c : int;
  615. r : ref Astring;
  616. # use current window if snarfing and its selection is non-null
  617. if(et!=t && dosnarf && et.w!=nil){
  618. if(et.w.body.q1>et.w.body.q0){
  619. t = et.w.body;
  620. t.file.mark(); # seq has been incremented by execute
  621. }
  622. else if(et.w.tag.q1>et.w.tag.q0)
  623. t = et.w.tag;
  624. }
  625. if(t == nil)
  626. return;
  627. locked = FALSE;
  628. if(t.w!=nil && et.w!=t.w){
  629. locked = TRUE;
  630. c = 'M';
  631. if(et.w != nil)
  632. c = et.w.owner;
  633. t.w.lock(c);
  634. }
  635. if(t.q0 == t.q1){
  636. if(locked)
  637. t.w.unlock();
  638. return;
  639. }
  640. if(dosnarf){
  641. q0 = t.q0;
  642. q1 = t.q1;
  643. snarfbuf.delete(0, snarfbuf.nc);
  644. r = stralloc(BUFSIZE);
  645. while(q0 < q1){
  646. n = q1 - q0;
  647. if(n > BUFSIZE)
  648. n = BUFSIZE;
  649. t.file.buf.read(q0, r, 0, n);
  650. snarfbuf.insert(snarfbuf.nc, r.s, n);
  651. q0 += n;
  652. }
  653. strfree(r);
  654. r = nil;
  655. acme->putsnarf();
  656. }
  657. if(docut){
  658. t.delete(t.q0, t.q1, TRUE);
  659. t.setselect(t.q0, t.q0);
  660. if(t.w != nil){
  661. scrl->scrdraw(t);
  662. t.w.settag();
  663. }
  664. }else if(dosnarf) # Snarf command
  665. dat->argtext = t;
  666. if(locked)
  667. t.w.unlock();
  668. }
  669. paste(et : ref Text, t : ref Text, selectall : int, tobody: int)
  670. {
  671. c : int;
  672. q, q0, q1, n : int;
  673. r : ref Astring;
  674. # if(tobody), use body of executing window (Paste or Send command)
  675. if(tobody && et!=nil && et.w!=nil){
  676. t = et.w.body;
  677. t.file.mark(); # seq has been incremented by execute
  678. }
  679. if(t == nil)
  680. return;
  681. acme->getsnarf();
  682. if(t==nil || snarfbuf.nc==0)
  683. return;
  684. if(t.w!=nil && et.w!=t.w){
  685. c = 'M';
  686. if(et.w != nil)
  687. c = et.w.owner;
  688. t.w.lock(c);
  689. }
  690. cut(t, t, FALSE, TRUE);
  691. q = 0;
  692. q0 = t.q0;
  693. q1 = t.q0+snarfbuf.nc;
  694. r = stralloc(BUFSIZE);
  695. while(q0 < q1){
  696. n = q1 - q0;
  697. if(n > BUFSIZE)
  698. n = BUFSIZE;
  699. if(r == nil)
  700. r = stralloc(n);
  701. snarfbuf.read(q, r, 0, n);
  702. t.insert(q0, r.s, n, TRUE, 0);
  703. q += n;
  704. q0 += n;
  705. }
  706. strfree(r);
  707. r = nil;
  708. if(selectall)
  709. t.setselect(t.q0, q1);
  710. else
  711. t.setselect(q1, q1);
  712. if(t.w != nil){
  713. scrl->scrdraw(t);
  714. t.w.settag();
  715. }
  716. if(t.w!=nil && et.w!=t.w)
  717. t.w.unlock();
  718. }
  719. look(et : ref Text, t : ref Text, argt : ref Text)
  720. {
  721. r : string;
  722. s : ref Astring;
  723. n : int;
  724. if(et != nil && et.w != nil){
  725. t = et.w.body;
  726. (nil, r, n) = getarg(argt, FALSE, FALSE);
  727. if(r == nil){
  728. n = t.q1-t.q0;
  729. s = stralloc(n);
  730. t.file.buf.read(t.q0, s, 0, n);
  731. r = s.s;
  732. }
  733. lookx->search(t, r, n);
  734. r = nil;
  735. }
  736. }
  737. send(et : ref Text, t : ref Text)
  738. {
  739. if(et.w==nil)
  740. return;
  741. t = et.w.body;
  742. if(t.q0 != t.q1)
  743. cut(t, t, TRUE, FALSE);
  744. t.setselect(t.file.buf.nc, t.file.buf.nc);
  745. paste(t, t, TRUE, TRUE);
  746. if(t.readc(t.file.buf.nc-1) != '\n'){
  747. t.insert(t.file.buf.nc, "\n", 1, TRUE, 0);
  748. t.setselect(t.file.buf.nc, t.file.buf.nc);
  749. }
  750. }
  751. edit(et: ref Text, argt: ref Text, arg: string, narg: int)
  752. {
  753. r: string;
  754. leng: int;
  755. if(et == nil)
  756. return;
  757. (nil, r, leng) = getarg(argt, FALSE, TRUE);
  758. seq++;
  759. if(r != nil){
  760. editm->editcmd(et, r, leng);
  761. r = nil;
  762. }else
  763. editm->editcmd(et, arg, narg);
  764. }
  765. exitx()
  766. {
  767. if(row.clean(TRUE))
  768. acme->acmeexit(nil);
  769. }
  770. putall()
  771. {
  772. i, j, e : int;
  773. w : ref Window;
  774. c : ref Column;
  775. a : string;
  776. for(i=0; i<row.ncol; i++){
  777. c = row.col[i];
  778. for(j=0; j<c.nw; j++){
  779. w = c.w[j];
  780. if(w.isscratch || w.isdir || len w.body.file.name==0)
  781. continue;
  782. if(w.nopen[QWevent] > byte 0)
  783. continue;
  784. a = w.body.file.name;
  785. e = utils->access(a);
  786. if(w.body.file.mod || w.body.ncache)
  787. if(e < 0)
  788. warning(nil, sprint("no auto-Put of %s: %r\n", a));
  789. else{
  790. w.commit(w.body);
  791. put(w.body, nil, nil, 0);
  792. }
  793. a = nil;
  794. }
  795. }
  796. }
  797. id(et : ref Text)
  798. {
  799. if(et != nil && et.w != nil)
  800. warning(nil, sprint("/mnt/acme/%d/\n", et.w.id));
  801. }
  802. limbo(et: ref Text)
  803. {
  804. s := getname(et.w.body, nil, nil, 0, 0);
  805. if(s == nil)
  806. return;
  807. for(l := len s; l > 0 && s[--l] != '/'; )
  808. ;
  809. if(s[l] == '/')
  810. s = s[l+1: ];
  811. s = "limbo -gw " + s;
  812. (dir, n) := dirname(et, nil, 0);
  813. if(n==1 && dir[0]=='.'){ # sigh
  814. dir = nil;
  815. n = 0;
  816. }
  817. spawn run(nil, s, dir, n, TRUE, nil, nil, FALSE);
  818. }
  819. local(et : ref Text, argt : ref Text, arg : string)
  820. {
  821. a, aa : string;
  822. dir : string;
  823. n : int;
  824. (aa, a) = getbytearg(argt, TRUE, TRUE);
  825. (dir, n) = dirname(et, nil, 0);
  826. if(n==1 && dir[0]=='.'){ # sigh
  827. dir = nil;
  828. n = 0;
  829. }
  830. spawn run(nil, arg, dir, n, FALSE, aa, a, FALSE);
  831. }
  832. kill(argt : ref Text, arg : string, narg : int)
  833. {
  834. a, cmd, r : string;
  835. na : int;
  836. (nil, r, na) = getarg(argt, FALSE, FALSE);
  837. if(r != nil)
  838. kill(nil, r, na);
  839. # loop condition: *arg is not a blank
  840. for(;;){
  841. (a, na) = findbl(arg, narg);
  842. if(a == arg)
  843. break;
  844. cmd = arg[0:narg-na];
  845. dat->ckill <-= cmd;
  846. (arg, narg) = skipbl(a, na);
  847. }
  848. }
  849. lineno(et : ref Text)
  850. {
  851. n : int;
  852. if (et == nil || et.w == nil || (et = et.w.body) == nil)
  853. return;
  854. q0 := et.q0;
  855. q1 := et.q1;
  856. if (q0 < 0 || q1 < 0 || q0 > q1)
  857. return;
  858. ln0 := 1;
  859. ln1 := 1;
  860. rp := stralloc(BUFSIZE);
  861. nc := et.file.buf.nc;
  862. if (q0 >= nc)
  863. q0 = nc-1;
  864. if (q1 >= nc)
  865. q1 = nc-1;
  866. for (q := 0; q < q1; ) {
  867. if (q+BUFSIZE > nc)
  868. n = nc-q;
  869. else
  870. n = BUFSIZE;
  871. et.file.buf.read(q, rp, 0, n);
  872. for (i := 0; i < n && q < q1; i++) {
  873. if (rp.s[i] == '\n') {
  874. if (q < q0)
  875. ln0++;
  876. if (q < q1-1)
  877. ln1++;
  878. }
  879. q++;
  880. }
  881. }
  882. rp = nil;
  883. if (et.file.name != nil)
  884. file := et.file.name + ":";
  885. else
  886. file = nil;
  887. if (ln0 == ln1)
  888. warning(nil, sprint("%s%d\n", file, ln0));
  889. else
  890. warning(nil, sprint("%s%d,%d\n", file, ln0, ln1));
  891. }
  892. fontx(et : ref Text, t : ref Text, argt : ref Text, arg : string, narg : int)
  893. {
  894. a, r, flag, file : string;
  895. na, nf : int;
  896. aa : string;
  897. newfont : ref Reffont;
  898. dp : ref Dat->Dirlist;
  899. i, fix : int;
  900. if(et==nil || et.w==nil)
  901. return;
  902. t = et.w.body;
  903. flag = nil;
  904. file = nil;
  905. # loop condition: *arg is not a blank
  906. nf = 0;
  907. for(;;){
  908. (a, na) = findbl(arg, narg);
  909. if(a == arg)
  910. break;
  911. r = arg[0:narg-na];
  912. if(r == "fix" || r == "var"){
  913. flag = nil;
  914. flag = r;
  915. }else{
  916. file = r;
  917. nf = narg-na;
  918. }
  919. (arg, narg) = skipbl(a, na);
  920. }
  921. (nil, r, na) = getarg(argt, FALSE, TRUE);
  922. if(r != nil)
  923. if(r == "fix" || r == "var"){
  924. flag = nil;
  925. flag = r;
  926. }else{
  927. file = r;
  928. nf = na;
  929. }
  930. fix = 1;
  931. if(flag != nil)
  932. fix = flag == "fix";
  933. else if(file == nil){
  934. newfont = Reffont.get(FALSE, FALSE, FALSE, nil);
  935. if(newfont != nil)
  936. fix = newfont.f.name == t.frame.font.name;
  937. }
  938. if(file != nil){
  939. aa = file[0:nf];
  940. newfont = Reffont.get(fix, flag!=nil, FALSE, aa);
  941. aa = nil;
  942. }else
  943. newfont = Reffont.get(fix, FALSE, FALSE, nil);
  944. if(newfont != nil){
  945. graph->draw(gui->mainwin, t.w.r, acme->textcols[Framem->BACK], nil, (0, 0));
  946. t.reffont.close();
  947. t.reffont = newfont;
  948. t.frame.font = newfont.f;
  949. if(t.w.isdir){
  950. t.all.min.x++; # force recolumnation; disgusting!
  951. for(i=0; i<t.w.ndl; i++){
  952. dp = t.w.dlp[i];
  953. aa = dp.r;
  954. dp.wid = graph->strwidth(newfont.f, aa);
  955. aa = nil;
  956. }
  957. }
  958. # avoid shrinking of window due to quantization
  959. t.w.col.grow(t.w, -1, 1);
  960. }
  961. file = nil;
  962. flag = nil;
  963. }
  964. incl(et : ref Text, argt : ref Text, arg : string, narg : int)
  965. {
  966. a, r : string;
  967. w : ref Window;
  968. na, n, leng : int;
  969. if(et==nil || et.w==nil)
  970. return;
  971. w = et.w;
  972. n = 0;
  973. (nil, r, leng) = getarg(argt, FALSE, TRUE);
  974. if(r != nil){
  975. n++;
  976. w.addincl(r, leng);
  977. }
  978. # loop condition: *arg is not a blank
  979. for(;;){
  980. (a, na) = findbl(arg, narg);
  981. if(a == arg)
  982. break;
  983. r = arg[0:narg-na];
  984. n++;
  985. w.addincl(r, narg-na);
  986. (arg, narg) = skipbl(a, na);
  987. }
  988. if(n==0 && w.nincl){
  989. for(n=w.nincl; --n>=0; )
  990. warning(nil, sprint("%s ", w.incl[n]));
  991. warning(nil, "\n");
  992. }
  993. }
  994. tab(et : ref Text, argt : ref Text, arg : string, narg : int)
  995. {
  996. a, r, p : string;
  997. w : ref Window;
  998. na, leng, tab : int;
  999. if(et==nil || et.w==nil)
  1000. return;
  1001. w = et.w;
  1002. (nil, r, leng) = getarg(argt, FALSE, TRUE);
  1003. tab = 0;
  1004. if(r!=nil && leng>0){
  1005. p = r[0:leng];
  1006. if('0'<=p[0] && p[0]<='9')
  1007. tab = int p;
  1008. p = nil;
  1009. }else{
  1010. (a, na) = findbl(arg, narg);
  1011. if(a != arg){
  1012. p = arg[0:narg-na];
  1013. if('0'<=p[0] && p[0]<='9')
  1014. tab = int p;
  1015. p = nil;
  1016. }
  1017. }
  1018. if(tab > 0){
  1019. if(w.body.tabstop != tab){
  1020. w.body.tabstop = tab;
  1021. w.reshape(w.r, 1);
  1022. }
  1023. }else
  1024. warning(nil, sys->sprint("%s: Tab %d\n", w.body.file.name, w.body.tabstop));
  1025. }
  1026. alphabet(et: ref Text, argt: ref Text, arg: string, narg: int)
  1027. {
  1028. r: string;
  1029. leng: int;
  1030. if(et == nil)
  1031. return;
  1032. (nil, r, leng) = getarg(argt, FALSE, FALSE);
  1033. if(r != nil)
  1034. setalphabet(r[0:leng]);
  1035. else
  1036. setalphabet(arg[0:narg]);
  1037. }
  1038. runfeed(p : array of ref Sys->FD, c : chan of int)
  1039. {
  1040. n : int;
  1041. buf : array of byte;
  1042. s : string;
  1043. sys->pctl(Sys->FORKFD, nil);
  1044. c <-= 1;
  1045. # p[1] = nil;
  1046. buf = array[256] of byte;
  1047. for(;;){
  1048. if((n = sys->read(p[0], buf, 256)) <= 0)
  1049. break;
  1050. s = string buf[0:n];
  1051. dat->cerr <-= s;
  1052. s = nil;
  1053. }
  1054. buf = nil;
  1055. exit;
  1056. }
  1057. run(win : ref Window, s : string, rdir : string, ndir : int, newns : int, argaddr : string, arg : string, iseditcmd: int)
  1058. {
  1059. c : ref Dat->Command;
  1060. name, dir : string;
  1061. e, t : int;
  1062. av : list of string;
  1063. r : int;
  1064. incl : array of string;
  1065. inarg, i, nincl : int;
  1066. tfd : ref Sys->FD;
  1067. p : array of ref Sys->FD;
  1068. pc : chan of int;
  1069. winid : int;
  1070. c = ref Dat->Command;
  1071. t = 0;
  1072. while(t < len s && (s[t]==' ' || s[t]=='\n' || s[t]=='\t'))
  1073. t++;
  1074. for(e=t; e < len s; e++)
  1075. if(s[e]==' ' || s[e]=='\n' || s[e]=='\t' )
  1076. break;
  1077. name = s[t:e];
  1078. e = utils->strrchr(name, '/');
  1079. if(e >= 0)
  1080. name = name[e+1:];
  1081. name += " "; # add blank here for ease in waittask
  1082. c.name = name;
  1083. name = nil;
  1084. pipechar := 0;
  1085. if (t < len s && (s[t] == '<' || s[t] == '|' || s[t] == '>')){
  1086. pipechar = s[t++];
  1087. s = s[t:];
  1088. }
  1089. c.pid = sys->pctl(0, nil);
  1090. c.iseditcmd = iseditcmd;
  1091. c.text = s;
  1092. dat->ccommand <-= c;
  1093. #
  1094. # must pctl() after communication because rendezvous name
  1095. # space is part of RFNAMEG.
  1096. #
  1097. if(newns){
  1098. wids : string = "";
  1099. filename: string;
  1100. if(win != nil){
  1101. filename = win.body.file.name;
  1102. wids = string win.id;
  1103. nincl = win.nincl;
  1104. incl = array[nincl] of string;
  1105. for(i=0; i<nincl; i++)
  1106. incl[i] = win.incl[i];
  1107. winid = win.id;
  1108. win.close();
  1109. }else{
  1110. winid = 0;
  1111. nincl = 0;
  1112. incl = nil;
  1113. if(dat->activewin != nil)
  1114. winid = (dat->activewin).id;
  1115. }
  1116. # sys->pctl(Sys->FORKNS|Sys->FORKFD|Sys->NEWPGRP, nil);
  1117. sys->pctl(Sys->FORKNS|Sys->NEWFD|Sys->FORKENV|Sys->NEWPGRP, 0::1::2::fsys->fsyscfd()::nil);
  1118. if(rdir != nil){
  1119. dir = rdir[0:ndir];
  1120. sys->chdir(dir); # ignore error: probably app. window
  1121. dir = nil;
  1122. }
  1123. if(filename != nil)
  1124. utils->setenv("%", filename);
  1125. c.md = fsys->fsysmount(rdir, ndir, incl, nincl);
  1126. if(c.md == nil){
  1127. # error("child: can't mount /mnt/acme");
  1128. warning(nil, "can't mount /mnt/acme");
  1129. exit;
  1130. }
  1131. if(winid > 0 && (pipechar=='|' || pipechar=='>')){
  1132. buf := sys->sprint("/mnt/acme/%d/rdsel", winid);
  1133. tfd = sys->open(buf, OREAD);
  1134. }
  1135. else
  1136. tfd = sys->open("/dev/null", OREAD);
  1137. sys->dup(tfd.fd, 0);
  1138. tfd = nil;
  1139. if((winid > 0 || iseditcmd) && (pipechar=='|' || pipechar=='<')){
  1140. buf: string;
  1141. if(iseditcmd){
  1142. if(winid > 0)
  1143. buf = sprint("/mnt/acme/%d/editout", winid);
  1144. else
  1145. buf = sprint("/mnt/acme/editout");
  1146. }
  1147. else
  1148. buf = sys->sprint("/mnt/acme/%d/wrsel", winid);
  1149. tfd = sys->open(buf, OWRITE);
  1150. }
  1151. else
  1152. tfd = sys->open("/dev/cons", OWRITE);
  1153. sys->dup(tfd.fd, 1);
  1154. tfd = nil;
  1155. if(winid > 0 && (pipechar=='|' || pipechar=='<')){
  1156. tfd = sys->open("/dev/cons", OWRITE);
  1157. sys->dup(tfd.fd, 2);
  1158. }
  1159. else
  1160. sys->dup(1, 2);
  1161. tfd = nil;
  1162. utils->setenv("acmewin", wids);
  1163. }else{
  1164. if(win != nil)
  1165. win.close();
  1166. sys->pctl(Sys->FORKFD|Sys->NEWPGRP, nil);
  1167. if(rdir != nil){
  1168. dir = rdir[0:ndir];
  1169. sys->chdir(dir); # ignore error: probably app. window
  1170. dir = nil;
  1171. }
  1172. p = array[2] of ref Sys->FD;
  1173. if(sys->pipe(p) < 0){
  1174. error("child: can't pipe");
  1175. exit;
  1176. }
  1177. pc = chan of int;
  1178. spawn runfeed(p, pc);
  1179. <-pc;
  1180. pc = nil;
  1181. fsys->fsysclose();
  1182. tfd = sys->open("/dev/null", OREAD);
  1183. sys->dup(tfd.fd, 0);
  1184. tfd = nil;
  1185. sys->dup(p[1].fd, 1);
  1186. sys->dup(1, 2);
  1187. p[0] = p[1] = nil;
  1188. }
  1189. if(argaddr != nil)
  1190. utils->setenv("acmeaddr", argaddr);
  1191. hard := 0;
  1192. if(len s > 512-10) # may need to print into stack
  1193. hard = 1;
  1194. else {
  1195. inarg = FALSE;
  1196. for(e=0; e < len s; e++){
  1197. r = s[e];
  1198. if(r==' ' || r=='\t')
  1199. continue;
  1200. if(r < ' ') {
  1201. hard = 1;
  1202. break;
  1203. }
  1204. if(utils->strchr("#;&|^$=`'{}()<>[]*?^~`", r) >= 0) {
  1205. hard = 1;
  1206. break;
  1207. }
  1208. inarg = TRUE;
  1209. }
  1210. if (!hard) {
  1211. if(!inarg)
  1212. exit;
  1213. av = nil;
  1214. sa := -1;
  1215. for(e=0; e < len s; e++){
  1216. r = s[e];
  1217. if(r==' ' || r=='\t'){
  1218. if (sa >= 0) {
  1219. av = s[sa:e] :: av;
  1220. sa = -1;
  1221. }
  1222. continue;
  1223. }
  1224. if (sa < 0)
  1225. sa = e;
  1226. }
  1227. if (sa >= 0)
  1228. av = s[sa:e] :: av;
  1229. if (arg != nil)
  1230. av = arg :: av;
  1231. av = utils->reverse(av);
  1232. c.av = av;
  1233. exec(hd av, av);
  1234. dat->cwait <-= string c.pid + " \"Exec\":";
  1235. exit;
  1236. }
  1237. }
  1238. if(arg != nil){
  1239. s = sprint("%s '%s'", s, arg); # BUG: what if quote in arg?
  1240. c.text = s;
  1241. }
  1242. av = nil;
  1243. av = s :: av;
  1244. av = "-c" :: av;
  1245. av = "/dis/sh" :: av;
  1246. exec(hd av, av);
  1247. dat->cwait <-= string c.pid + " \"Exec\":";
  1248. exit;
  1249. }
  1250. # Nasty bug causes
  1251. # Edit ,|nonexistentcommand
  1252. # (or ,> or ,<) to lock up acme. Easy fix. Add these two lines
  1253. # to the failure case of runwaittask():
  1254. #
  1255. # /sys/src/cmd/acme/exec.c:1287 a exec.c:1288,1289
  1256. # else{
  1257. # if(c->iseditcmd)
  1258. # sendul(cedit, 0);
  1259. # free(c->name);
  1260. # free(c->text);
  1261. # free(c);
  1262. # }