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