sam.c 12 KB

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