sam.c 12 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716
  1. #include "sam.h"
  2. Rune genbuf[BLOCKSIZE];
  3. int io;
  4. int panicking;
  5. int rescuing;
  6. String genstr;
  7. String rhs;
  8. String curwd;
  9. String cmdstr;
  10. Rune empty[] = { 0 };
  11. char *genc;
  12. File *curfile;
  13. File *flist;
  14. File *cmd;
  15. jmp_buf mainloop;
  16. List tempfile = { 'p' };
  17. int quitok = TRUE;
  18. int downloaded;
  19. int dflag;
  20. int Rflag;
  21. char *machine;
  22. char *home;
  23. int bpipeok;
  24. int termlocked;
  25. char *samterm = SAMTERM;
  26. char *rsamname = RSAM;
  27. File *lastfile;
  28. Disk *disk;
  29. long seq;
  30. Rune baddir[] = { '<', 'b', 'a', 'd', 'd', 'i', 'r', '>', '\n'};
  31. void usage(void);
  32. void main(int argc, char *argv[])
  33. {
  34. int i;
  35. String *t;
  36. char *termargs[10], **ap;
  37. ap = termargs;
  38. *ap++ = "samterm";
  39. ARGBEGIN{
  40. case 'd':
  41. dflag++;
  42. break;
  43. case 'r':
  44. machine = EARGF(usage());
  45. break;
  46. case 'R':
  47. Rflag++;
  48. break;
  49. case 't':
  50. samterm = EARGF(usage());
  51. break;
  52. case 's':
  53. rsamname = EARGF(usage());
  54. break;
  55. default:
  56. dprint("sam: unknown flag %c\n", ARGC());
  57. usage();
  58. /* options for samterm */
  59. case 'a':
  60. *ap++ = "-a";
  61. break;
  62. }ARGEND
  63. *ap = nil;
  64. Strinit(&cmdstr);
  65. Strinit0(&lastpat);
  66. Strinit0(&lastregexp);
  67. Strinit0(&genstr);
  68. Strinit0(&rhs);
  69. Strinit0(&curwd);
  70. Strinit0(&plan9cmd);
  71. home = getenv(HOME);
  72. disk = diskinit();
  73. if(home == 0)
  74. home = "/";
  75. if(!dflag)
  76. startup(machine, Rflag, termargs, argv);
  77. notify(notifyf);
  78. getcurwd();
  79. if(argc>0){
  80. for(i=0; i<argc; i++){
  81. if(!setjmp(mainloop)){
  82. t = tmpcstr(argv[i]);
  83. Straddc(t, '\0');
  84. Strduplstr(&genstr, t);
  85. freetmpstr(t);
  86. fixname(&genstr);
  87. logsetname(newfile(), &genstr);
  88. }
  89. }
  90. }else if(!downloaded)
  91. newfile();
  92. seq++;
  93. if(file.nused)
  94. current(file.filepptr[0]);
  95. setjmp(mainloop);
  96. cmdloop();
  97. trytoquit(); /* if we already q'ed, quitok will be TRUE */
  98. exits(0);
  99. }
  100. void
  101. usage(void)
  102. {
  103. dprint("usage: sam [-d] [-t samterm] [-s sam name] -r machine\n");
  104. exits("usage");
  105. }
  106. void
  107. rescue(void)
  108. {
  109. int i, nblank = 0;
  110. File *f;
  111. char *c;
  112. char buf[256];
  113. if(rescuing++)
  114. return;
  115. io = -1;
  116. for(i=0; i<file.nused; i++){
  117. f = file.filepptr[i];
  118. if(f==cmd || f->nc==0 || !fileisdirty(f))
  119. continue;
  120. if(io == -1){
  121. sprint(buf, "%s/sam.save", home);
  122. io = create(buf, 1, 0777);
  123. if(io<0)
  124. return;
  125. }
  126. if(f->name.s[0]){
  127. c = Strtoc(&f->name);
  128. strncpy(buf, c, sizeof buf-1);
  129. buf[sizeof buf-1] = 0;
  130. free(c);
  131. }else
  132. sprint(buf, "nameless.%d", nblank++);
  133. fprint(io, "#!%s '%s' $* <<'---%s'\n", SAMSAVECMD, buf, buf);
  134. addr.r.p1 = 0, addr.r.p2 = f->nc;
  135. writeio(f);
  136. fprint(io, "\n---%s\n", (char *)buf);
  137. }
  138. }
  139. void
  140. panic(char *s)
  141. {
  142. int wasd;
  143. if(!panicking++ && !setjmp(mainloop)){
  144. wasd = downloaded;
  145. downloaded = 0;
  146. dprint("sam: panic: %s: %r\n", s);
  147. if(wasd)
  148. fprint(2, "sam: panic: %s: %r\n", s);
  149. rescue();
  150. abort();
  151. }
  152. }
  153. void
  154. hiccough(char *s)
  155. {
  156. File *f;
  157. int i;
  158. if(rescuing)
  159. exits("rescue");
  160. if(s)
  161. dprint("%s\n", s);
  162. resetcmd();
  163. resetxec();
  164. resetsys();
  165. if(io > 0)
  166. close(io);
  167. /*
  168. * back out any logged changes & restore old sequences
  169. */
  170. for(i=0; i<file.nused; i++){
  171. f = file.filepptr[i];
  172. if(f==cmd)
  173. continue;
  174. if(f->seq==seq){
  175. bufdelete(&f->epsilon, 0, f->epsilon.nc);
  176. f->seq = f->prevseq;
  177. f->dot.r = f->prevdot;
  178. f->mark = f->prevmark;
  179. state(f, f->prevmod ? Dirty: Clean);
  180. }
  181. }
  182. update();
  183. if (curfile) {
  184. if (curfile->unread)
  185. curfile->unread = FALSE;
  186. else if (downloaded)
  187. outTs(Hcurrent, curfile->tag);
  188. }
  189. longjmp(mainloop, 1);
  190. }
  191. void
  192. intr(void)
  193. {
  194. error(Eintr);
  195. }
  196. void
  197. trytoclose(File *f)
  198. {
  199. char *t;
  200. char buf[256];
  201. if(f == cmd) /* possible? */
  202. return;
  203. if(f->deleted)
  204. return;
  205. if(fileisdirty(f) && !f->closeok){
  206. f->closeok = TRUE;
  207. if(f->name.s[0]){
  208. t = Strtoc(&f->name);
  209. strncpy(buf, t, sizeof buf-1);
  210. free(t);
  211. }else
  212. strcpy(buf, "nameless file");
  213. error_s(Emodified, buf);
  214. }
  215. f->deleted = TRUE;
  216. }
  217. void
  218. trytoquit(void)
  219. {
  220. int c;
  221. File *f;
  222. if(!quitok){
  223. for(c = 0; c<file.nused; c++){
  224. f = file.filepptr[c];
  225. if(f!=cmd && fileisdirty(f)){
  226. quitok = TRUE;
  227. eof = FALSE;
  228. error(Echanges);
  229. }
  230. }
  231. }
  232. }
  233. void
  234. load(File *f)
  235. {
  236. Address saveaddr;
  237. Strduplstr(&genstr, &f->name);
  238. filename(f);
  239. if(f->name.s[0]){
  240. saveaddr = addr;
  241. edit(f, 'I');
  242. addr = saveaddr;
  243. }else{
  244. f->unread = 0;
  245. f->cleanseq = f->seq;
  246. }
  247. fileupdate(f, TRUE, TRUE);
  248. }
  249. void
  250. cmdupdate(void)
  251. {
  252. if(cmd && cmd->seq!=0){
  253. fileupdate(cmd, FALSE, downloaded);
  254. cmd->dot.r.p1 = cmd->dot.r.p2 = cmd->nc;
  255. telldot(cmd);
  256. }
  257. }
  258. void
  259. delete(File *f)
  260. {
  261. if(downloaded && f->rasp)
  262. outTs(Hclose, f->tag);
  263. delfile(f);
  264. if(f == curfile)
  265. current(0);
  266. }
  267. void
  268. update(void)
  269. {
  270. int i, anymod;
  271. File *f;
  272. settempfile();
  273. for(anymod = i=0; i<tempfile.nused; i++){
  274. f = tempfile.filepptr[i];
  275. if(f==cmd) /* cmd gets done in main() */
  276. continue;
  277. if(f->deleted) {
  278. delete(f);
  279. continue;
  280. }
  281. if(f->seq==seq && fileupdate(f, FALSE, downloaded))
  282. anymod++;
  283. if(f->rasp)
  284. telldot(f);
  285. }
  286. if(anymod)
  287. seq++;
  288. }
  289. File *
  290. current(File *f)
  291. {
  292. return curfile = f;
  293. }
  294. void
  295. edit(File *f, int cmd)
  296. {
  297. int empty = TRUE;
  298. Posn p;
  299. int nulls;
  300. if(cmd == 'r')
  301. logdelete(f, addr.r.p1, addr.r.p2);
  302. if(cmd=='e' || cmd=='I'){
  303. logdelete(f, (Posn)0, f->nc);
  304. addr.r.p2 = f->nc;
  305. }else if(f->nc!=0 || (f->name.s[0] && Strcmp(&genstr, &f->name)!=0))
  306. empty = FALSE;
  307. if((io = open(genc, OREAD))<0) {
  308. if (curfile && curfile->unread)
  309. curfile->unread = FALSE;
  310. error_r(Eopen, genc);
  311. }
  312. p = readio(f, &nulls, empty, TRUE);
  313. closeio((cmd=='e' || cmd=='I')? -1 : p);
  314. if(cmd == 'r')
  315. f->ndot.r.p1 = addr.r.p2, f->ndot.r.p2 = addr.r.p2+p;
  316. else
  317. f->ndot.r.p1 = f->ndot.r.p2 = 0;
  318. f->closeok = empty;
  319. if (quitok)
  320. quitok = empty;
  321. else
  322. quitok = FALSE;
  323. state(f, empty && !nulls? Clean : Dirty);
  324. if(empty && !nulls)
  325. f->cleanseq = f->seq;
  326. if(cmd == 'e')
  327. filename(f);
  328. }
  329. int
  330. getname(File *f, String *s, int save)
  331. {
  332. int c, i;
  333. Strzero(&genstr);
  334. if(genc){
  335. free(genc);
  336. genc = 0;
  337. }
  338. if(s==0 || (c = s->s[0])==0){ /* no name provided */
  339. if(f)
  340. Strduplstr(&genstr, &f->name);
  341. goto Return;
  342. }
  343. if(c!=' ' && c!='\t')
  344. error(Eblank);
  345. for(i=0; (c=s->s[i])==' ' || c=='\t'; i++)
  346. ;
  347. while(s->s[i] > ' ')
  348. Straddc(&genstr, s->s[i++]);
  349. if(s->s[i])
  350. error(Enewline);
  351. fixname(&genstr);
  352. if(f && (save || f->name.s[0]==0)){
  353. logsetname(f, &genstr);
  354. if(Strcmp(&f->name, &genstr)){
  355. quitok = f->closeok = FALSE;
  356. f->qidpath = 0;
  357. f->mtime = 0;
  358. state(f, Dirty); /* if it's 'e', fix later */
  359. }
  360. }
  361. Return:
  362. genc = Strtoc(&genstr);
  363. i = genstr.n;
  364. if(i && genstr.s[i-1]==0)
  365. i--;
  366. return i; /* strlen(name) */
  367. }
  368. void
  369. filename(File *f)
  370. {
  371. if(genc)
  372. free(genc);
  373. genc = Strtoc(&genstr);
  374. dprint("%c%c%c %s\n", " '"[f->mod],
  375. "-+"[f->rasp!=0], " ."[f==curfile], genc);
  376. }
  377. void
  378. undostep(File *f, int isundo)
  379. {
  380. uint p1, p2;
  381. int mod;
  382. mod = f->mod;
  383. fileundo(f, isundo, 1, &p1, &p2, TRUE);
  384. f->ndot = f->dot;
  385. if(f->mod){
  386. f->closeok = 0;
  387. quitok = 0;
  388. }else
  389. f->closeok = 1;
  390. if(f->mod != mod){
  391. f->mod = mod;
  392. if(mod)
  393. mod = Clean;
  394. else
  395. mod = Dirty;
  396. state(f, mod);
  397. }
  398. }
  399. int
  400. undo(int isundo)
  401. {
  402. File *f;
  403. int i;
  404. Mod max;
  405. max = undoseq(curfile, isundo);
  406. if(max == 0)
  407. return 0;
  408. settempfile();
  409. for(i = 0; i<tempfile.nused; i++){
  410. f = tempfile.filepptr[i];
  411. if(f!=cmd && undoseq(f, isundo)==max)
  412. undostep(f, isundo);
  413. }
  414. return 1;
  415. }
  416. int
  417. readcmd(String *s)
  418. {
  419. int retcode;
  420. if(flist != 0)
  421. fileclose(flist);
  422. flist = fileopen();
  423. addr.r.p1 = 0, addr.r.p2 = flist->nc;
  424. retcode = plan9(flist, '<', s, FALSE);
  425. fileupdate(flist, FALSE, FALSE);
  426. flist->seq = 0;
  427. if (flist->nc > BLOCKSIZE)
  428. error(Etoolong);
  429. Strzero(&genstr);
  430. Strinsure(&genstr, flist->nc);
  431. bufread(flist, (Posn)0, genbuf, flist->nc);
  432. memmove(genstr.s, genbuf, flist->nc*RUNESIZE);
  433. genstr.n = flist->nc;
  434. Straddc(&genstr, '\0');
  435. return retcode;
  436. }
  437. void
  438. getcurwd(void)
  439. {
  440. String *t;
  441. char buf[256];
  442. buf[0] = 0;
  443. getwd(buf, sizeof(buf));
  444. t = tmpcstr(buf);
  445. Strduplstr(&curwd, t);
  446. freetmpstr(t);
  447. if(curwd.n == 0)
  448. warn(Wpwd);
  449. else if(curwd.s[curwd.n-1] != '/')
  450. Straddc(&curwd, '/');
  451. }
  452. void
  453. cd(String *str)
  454. {
  455. int i, fd;
  456. char *s;
  457. File *f;
  458. String owd;
  459. getcurwd();
  460. if(getname((File *)0, str, FALSE))
  461. s = genc;
  462. else
  463. s = home;
  464. if(chdir(s))
  465. syserror("chdir");
  466. fd = open("/dev/wdir", OWRITE);
  467. if(fd > 0)
  468. write(fd, s, strlen(s));
  469. dprint("!\n");
  470. Strinit(&owd);
  471. Strduplstr(&owd, &curwd);
  472. getcurwd();
  473. settempfile();
  474. for(i=0; i<tempfile.nused; i++){
  475. f = tempfile.filepptr[i];
  476. if(f!=cmd && f->name.s[0]!='/' && f->name.s[0]!=0){
  477. Strinsert(&f->name, &owd, (Posn)0);
  478. fixname(&f->name);
  479. sortname(f);
  480. }else if(f != cmd && Strispre(&curwd, &f->name)){
  481. fixname(&f->name);
  482. sortname(f);
  483. }
  484. }
  485. Strclose(&owd);
  486. }
  487. int
  488. loadflist(String *s)
  489. {
  490. int c, i;
  491. c = s->s[0];
  492. for(i = 0; s->s[i]==' ' || s->s[i]=='\t'; i++)
  493. ;
  494. if((c==' ' || c=='\t') && s->s[i]!='\n'){
  495. if(s->s[i]=='<'){
  496. Strdelete(s, 0L, (long)i+1);
  497. readcmd(s);
  498. }else{
  499. Strzero(&genstr);
  500. while((c = s->s[i++]) && c!='\n')
  501. Straddc(&genstr, c);
  502. Straddc(&genstr, '\0');
  503. }
  504. }else{
  505. if(c != '\n')
  506. error(Eblank);
  507. Strdupl(&genstr, empty);
  508. }
  509. if(genc)
  510. free(genc);
  511. genc = Strtoc(&genstr);
  512. return genstr.s[0];
  513. }
  514. File *
  515. readflist(int readall, int delete)
  516. {
  517. Posn i;
  518. int c;
  519. File *f;
  520. String t;
  521. Strinit(&t);
  522. for(i=0,f=0; f==0 || readall || delete; i++){ /* ++ skips blank */
  523. Strdelete(&genstr, (Posn)0, i);
  524. for(i=0; (c = genstr.s[i])==' ' || c=='\t' || c=='\n'; i++)
  525. ;
  526. if(i >= genstr.n)
  527. break;
  528. Strdelete(&genstr, (Posn)0, i);
  529. for(i=0; (c=genstr.s[i]) && c!=' ' && c!='\t' && c!='\n'; i++)
  530. ;
  531. if(i == 0)
  532. break;
  533. genstr.s[i] = 0;
  534. Strduplstr(&t, tmprstr(genstr.s, i+1));
  535. fixname(&t);
  536. f = lookfile(&t);
  537. if(delete){
  538. if(f == 0)
  539. warn_S(Wfile, &t);
  540. else
  541. trytoclose(f);
  542. }else if(f==0 && readall)
  543. logsetname(f = newfile(), &t);
  544. }
  545. Strclose(&t);
  546. return f;
  547. }
  548. File *
  549. tofile(String *s)
  550. {
  551. File *f;
  552. if(s->s[0] != ' ')
  553. error(Eblank);
  554. if(loadflist(s) == 0){
  555. f = lookfile(&genstr); /* empty string ==> nameless file */
  556. if(f == 0)
  557. error_s(Emenu, genc);
  558. }else if((f=readflist(FALSE, FALSE)) == 0)
  559. error_s(Emenu, genc);
  560. return current(f);
  561. }
  562. File *
  563. getfile(String *s)
  564. {
  565. File *f;
  566. if(loadflist(s) == 0)
  567. logsetname(f = newfile(), &genstr);
  568. else if((f=readflist(TRUE, FALSE)) == 0)
  569. error(Eblank);
  570. return current(f);
  571. }
  572. void
  573. closefiles(File *f, String *s)
  574. {
  575. if(s->s[0] == 0){
  576. if(f == 0)
  577. error(Enofile);
  578. trytoclose(f);
  579. return;
  580. }
  581. if(s->s[0] != ' ')
  582. error(Eblank);
  583. if(loadflist(s) == 0)
  584. error(Enewline);
  585. readflist(FALSE, TRUE);
  586. }
  587. void
  588. copy(File *f, Address addr2)
  589. {
  590. Posn p;
  591. int ni;
  592. for(p=addr.r.p1; p<addr.r.p2; p+=ni){
  593. ni = addr.r.p2-p;
  594. if(ni > BLOCKSIZE)
  595. ni = BLOCKSIZE;
  596. bufread(f, p, genbuf, ni);
  597. loginsert(addr2.f, addr2.r.p2, tmprstr(genbuf, ni)->s, ni);
  598. }
  599. addr2.f->ndot.r.p2 = addr2.r.p2+(f->dot.r.p2-f->dot.r.p1);
  600. addr2.f->ndot.r.p1 = addr2.r.p2;
  601. }
  602. void
  603. move(File *f, Address addr2)
  604. {
  605. if(addr.r.p2 <= addr2.r.p2){
  606. logdelete(f, addr.r.p1, addr.r.p2);
  607. copy(f, addr2);
  608. }else if(addr.r.p1 >= addr2.r.p2){
  609. copy(f, addr2);
  610. logdelete(f, addr.r.p1, addr.r.p2);
  611. }else
  612. error(Eoverlap);
  613. }
  614. Posn
  615. nlcount(File *f, Posn p0, Posn p1)
  616. {
  617. Posn nl = 0;
  618. while(p0 < p1)
  619. if(filereadc(f, p0++)=='\n')
  620. nl++;
  621. return nl;
  622. }
  623. void
  624. printposn(File *f, int charsonly)
  625. {
  626. Posn l1, l2;
  627. if(!charsonly){
  628. l1 = 1+nlcount(f, (Posn)0, addr.r.p1);
  629. l2 = l1+nlcount(f, addr.r.p1, addr.r.p2);
  630. /* check if addr ends with '\n' */
  631. if(addr.r.p2>0 && addr.r.p2>addr.r.p1 && filereadc(f, addr.r.p2-1)=='\n')
  632. --l2;
  633. dprint("%lud", l1);
  634. if(l2 != l1)
  635. dprint(",%lud", l2);
  636. dprint("; ");
  637. }
  638. dprint("#%lud", addr.r.p1);
  639. if(addr.r.p2 != addr.r.p1)
  640. dprint(",#%lud", addr.r.p2);
  641. dprint("\n");
  642. }
  643. void
  644. settempfile(void)
  645. {
  646. if(tempfile.nalloc < file.nused){
  647. if(tempfile.filepptr)
  648. free(tempfile.filepptr);
  649. tempfile.filepptr = emalloc(sizeof(File*)*file.nused);
  650. tempfile.nalloc = file.nused;
  651. }
  652. memmove(tempfile.filepptr, file.filepptr, sizeof(File*)*file.nused);
  653. tempfile.nused = file.nused;
  654. }