ttags.c 19 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029
  1. #include "lib9.h"
  2. #include "draw.h"
  3. #include "tk.h"
  4. #include "textw.h"
  5. #define istring u.string
  6. #define iwin u.win
  7. #define imark u.mark
  8. #define iline u.line
  9. static char* tkttagadd(Tk*, char*, char**);
  10. static char* tkttagbind(Tk*, char*, char**);
  11. static char* tkttagcget(Tk*, char*, char**);
  12. static char* tkttagconfigure(Tk*, char*, char**);
  13. static char* tkttagdelete(Tk*, char*, char**);
  14. static char* tkttaglower(Tk*, char*, char**);
  15. static char* tkttagnames(Tk*, char*, char**);
  16. static char* tkttagnextrange(Tk*, char*, char**);
  17. static char* tkttagprevrange(Tk*, char*, char**);
  18. static char* tkttagraise(Tk*, char*, char**);
  19. static char* tkttagranges(Tk*, char*, char**);
  20. static char* tkttagremove(Tk*, char*, char**);
  21. #define O(t, e) ((long)(&((t*)0)->e))
  22. #define TKTEO (O(TkTtaginfo, env))
  23. static
  24. TkOption tagopts[] =
  25. {
  26. "borderwidth",
  27. OPTnndist, O(TkTtaginfo, opts[TkTborderwidth]), nil,
  28. "justify",
  29. OPTstab, O(TkTtaginfo, opts[TkTjustify]), tkjustify,
  30. "lineheight",
  31. OPTnndist, O(TkTtaginfo, opts[TkTlineheight]), IAUX(TKTEO),
  32. "lmargin1",
  33. OPTdist, O(TkTtaginfo, opts[TkTlmargin1]), IAUX(TKTEO),
  34. "lmargin2",
  35. OPTdist, O(TkTtaginfo, opts[TkTlmargin2]), IAUX(TKTEO),
  36. "lmargin3",
  37. OPTdist, O(TkTtaginfo, opts[TkTlmargin3]), IAUX(TKTEO),
  38. "rmargin",
  39. OPTdist, O(TkTtaginfo, opts[TkTrmargin]), IAUX(TKTEO),
  40. "spacing1",
  41. OPTnndist, O(TkTtaginfo, opts[TkTspacing1]), IAUX(TKTEO),
  42. "spacing2",
  43. OPTnndist, O(TkTtaginfo, opts[TkTspacing2]), IAUX(TKTEO),
  44. "spacing3",
  45. OPTnndist, O(TkTtaginfo, opts[TkTspacing3]), IAUX(TKTEO),
  46. "offset",
  47. OPTdist, O(TkTtaginfo, opts[TkToffset]), IAUX(TKTEO),
  48. "underline",
  49. OPTstab, O(TkTtaginfo, opts[TkTunderline]), tkbool,
  50. "overstrike",
  51. OPTstab, O(TkTtaginfo, opts[TkToverstrike]), tkbool,
  52. "relief",
  53. OPTstab, O(TkTtaginfo, opts[TkTrelief]), tkrelief,
  54. "tabs",
  55. OPTtabs, O(TkTtaginfo, tabs), IAUX(TKTEO),
  56. "wrap",
  57. OPTstab, O(TkTtaginfo, opts[TkTwrap]), tkwrap,
  58. nil,
  59. };
  60. static
  61. TkOption tagenvopts[] =
  62. {
  63. "foreground", OPTcolr, O(TkTtaginfo, env), IAUX(TkCforegnd),
  64. "background", OPTcolr, O(TkTtaginfo, env), IAUX(TkCbackgnd),
  65. "fg", OPTcolr, O(TkTtaginfo, env), IAUX(TkCforegnd),
  66. "bg", OPTcolr, O(TkTtaginfo, env), IAUX(TkCbackgnd),
  67. "font", OPTfont, O(TkTtaginfo, env), nil,
  68. nil
  69. };
  70. TkCmdtab
  71. tkttagcmd[] =
  72. {
  73. "add", tkttagadd,
  74. "bind", tkttagbind,
  75. "cget", tkttagcget,
  76. "configure", tkttagconfigure,
  77. "delete", tkttagdelete,
  78. "lower", tkttaglower,
  79. "names", tkttagnames,
  80. "nextrange", tkttagnextrange,
  81. "prevrange", tkttagprevrange,
  82. "raise", tkttagraise,
  83. "ranges", tkttagranges,
  84. "remove", tkttagremove,
  85. nil
  86. };
  87. int
  88. tktanytags(TkTitem *it)
  89. {
  90. int i;
  91. if(it->tagextra == 0)
  92. return (it->tags[0] != 0);
  93. for(i = 0; i <= it->tagextra; i++)
  94. if(it->tags[i] != 0)
  95. return 1;
  96. return 0;
  97. }
  98. int
  99. tktsametags(TkTitem *i1, TkTitem *i2)
  100. {
  101. int i, j;
  102. for(i = 0; i <= i1->tagextra && i <= i2->tagextra; i++)
  103. if(i1->tags[i] != i2->tags[i])
  104. return 0;
  105. for(j = i; j <= i1->tagextra; j++)
  106. if(i1->tags[j] != 0)
  107. return 0;
  108. for(j = i; j <= i2->tagextra; j++)
  109. if(i2->tags[j] != 0)
  110. return 0;
  111. return 1;
  112. }
  113. int
  114. tkttagset(TkTitem *it, int id)
  115. {
  116. int i;
  117. if(it->tagextra == 0 && it->tags[0] == 0)
  118. return 0;
  119. for(i = 0; i <= it->tagextra; i++) {
  120. if(id < 32)
  121. return ((it->tags[i] & (1<<id)) != 0);
  122. id -= 32;
  123. }
  124. return 0;
  125. }
  126. char *
  127. tkttagname(TkText *tkt, int id)
  128. {
  129. TkTtaginfo *t;
  130. for(t = tkt->tags; t != nil; t = t->next) {
  131. if(t->id == id)
  132. return t->name;
  133. }
  134. return "";
  135. }
  136. /* return 1 if this actually changes the value */
  137. int
  138. tkttagbit(TkTitem *it, int id, int val)
  139. {
  140. int i, changed;
  141. ulong z, b;
  142. changed = 0;
  143. for(i = 0; i <= it->tagextra; i++) {
  144. if(id < 32) {
  145. b = (1<<id);
  146. z = it->tags[i];
  147. if(val == 0) {
  148. if(z & b) {
  149. changed = 1;
  150. it->tags[i] = z & (~b);
  151. }
  152. }
  153. else {
  154. if((z & b) == 0) {
  155. changed = 1;
  156. it->tags[i] = z | b;
  157. }
  158. }
  159. break;
  160. }
  161. id -= 32;
  162. }
  163. return changed;
  164. }
  165. void
  166. tkttagcomb(TkTitem *i1, TkTitem *i2, int add)
  167. {
  168. int i;
  169. for(i = 0; i <= i1->tagextra && i <= i2->tagextra; i++) {
  170. if(add == 1)
  171. i1->tags[i] |= i2->tags[i];
  172. else if(add == 0)
  173. /* intersect */
  174. i1->tags[i] &= i2->tags[i];
  175. else
  176. /* subtract */
  177. i1->tags[i] &= ~i2->tags[i];
  178. }
  179. }
  180. char*
  181. tktaddtaginfo(Tk *tk, char *name, TkTtaginfo **ret)
  182. {
  183. int i, *ntagp;
  184. TkTtaginfo *ti;
  185. TkText *tkt, *tktshare;
  186. tkt = TKobj(TkText, tk);
  187. ti = malloc(sizeof(TkTtaginfo));
  188. if(ti == nil)
  189. return TkNomem;
  190. ntagp = &tkt->nexttag;
  191. if(tkt->tagshare != nil) {
  192. tktshare = TKobj(TkText, tkt->tagshare);
  193. ntagp = &tktshare->nexttag;
  194. }
  195. ti->id = *ntagp;
  196. ti->name = strdup(name);
  197. if(ti->name == nil) {
  198. free(ti);
  199. return TkNomem;
  200. }
  201. ti->env = tknewenv(tk->env->top);
  202. if(ti->env == nil) {
  203. free(ti->name);
  204. free(ti);
  205. return TkNomem;
  206. }
  207. ti->tabs = nil;
  208. for(i = 0; i < TkTnumopts; i++)
  209. ti->opts[i] = TkTunset;
  210. ti->next = tkt->tags;
  211. tkt->tags = ti;
  212. (*ntagp)++;
  213. if(tkt->tagshare)
  214. tkt->nexttag = *ntagp;
  215. *ret = ti;
  216. return nil;
  217. }
  218. TkTtaginfo *
  219. tktfindtag(TkTtaginfo *t, char *name)
  220. {
  221. while(t != nil) {
  222. if(strcmp(t->name, name) == 0)
  223. return t;
  224. t = t->next;
  225. }
  226. return nil;
  227. }
  228. void
  229. tktfreetags(TkTtaginfo *t)
  230. {
  231. TkTtaginfo *n;
  232. while(t != nil) {
  233. n = t->next;
  234. free(t->name);
  235. tktfreetabs(t->tabs);
  236. tkputenv(t->env);
  237. tkfreebind(t->binds);
  238. free(t);
  239. t = n;
  240. }
  241. }
  242. int
  243. tkttagind(Tk *tk, char *name, int first, TkTindex *ans)
  244. {
  245. int id;
  246. TkTtaginfo *t;
  247. TkText *tkt;
  248. tkt = TKobj(TkText, tk);
  249. if(strcmp(name, "sel") == 0) {
  250. if(tkt->selfirst == nil)
  251. return 0;
  252. if(first)
  253. tktitemind(tkt->selfirst, ans);
  254. else
  255. tktitemind(tkt->sellast, ans);
  256. return 1;
  257. }
  258. t = tktfindtag(tkt->tags, name);
  259. if(t == nil)
  260. return 0;
  261. id = t->id;
  262. if(first) {
  263. tktstartind(tkt, ans);
  264. while(!tkttagset(ans->item, id))
  265. if(!tktadjustind(tkt, TkTbyitem, ans))
  266. return 0;
  267. }
  268. else {
  269. tktendind(tkt, ans);
  270. while(!tkttagset(ans->item, id))
  271. if(!tktadjustind(tkt, TkTbyitemback, ans))
  272. return 0;
  273. tktadjustind(tkt, TkTbyitem, ans);
  274. }
  275. return 1;
  276. }
  277. /*
  278. * Fill in opts and e, based on info from tags set in it,
  279. * using tags order for priority.
  280. * If dflt != 0, options not set are filled from tk,
  281. * otherwise iInteger options not set by any tag are left 'TkTunset'
  282. * and environment values not set are left nil.
  283. */
  284. void
  285. tkttagopts(Tk *tk, TkTitem *it, int *opts, TkEnv *e, TkTtabstop **tb, int dflt)
  286. {
  287. int i;
  288. int colset;
  289. TkEnv *te;
  290. TkTtaginfo *tags;
  291. TkText *tkt = TKobj(TkText, tk);
  292. if (tb != nil)
  293. *tb = tkt->tabs;
  294. tags = tkt->tags;
  295. if(opts != nil)
  296. for(i = 0; i < TkTnumopts; i++)
  297. opts[i] = TkTunset;
  298. memset(e, 0, sizeof(TkEnv));
  299. e->top = tk->env->top;
  300. colset = 0;
  301. while(tags != nil) {
  302. if(tkttagset(it, tags->id)) {
  303. if(opts != nil) {
  304. for(i = 0; i < TkTnumopts; i++) {
  305. if(opts[i] == TkTunset && tags->opts[i] != TkTunset)
  306. opts[i] = tags->opts[i];
  307. }
  308. }
  309. te = tags->env;
  310. for(i = 0; i < TkNcolor; i++)
  311. if(!(colset & (1<<i)) && te->set & (1<<i)) {
  312. e->colors[i] = te->colors[i];
  313. colset |= 1<<i;
  314. }
  315. if(e->font == nil && te->font != nil)
  316. e->font = te->font;
  317. if (tb != nil && tags->tabs != nil)
  318. *tb = tags->tabs;
  319. }
  320. tags = tags->next;
  321. }
  322. e->set |= colset;
  323. if(dflt) {
  324. if(opts != nil) {
  325. for(i = 0; i < TkTnumopts; i++)
  326. if(opts[i] == TkTunset)
  327. opts[i] = tkt->opts[i];
  328. }
  329. te = tk->env;
  330. for(i = 0; i < TkNcolor; i++)
  331. if(!(e->set & (1<<i))) {
  332. e->colors[i] = te->colors[i];
  333. e->set |= 1<<i;
  334. }
  335. if(e->font == nil)
  336. e->font = te->font;
  337. }
  338. }
  339. char*
  340. tkttagparse(Tk *tk, char **parg, TkTtaginfo **ret)
  341. {
  342. char *e, *buf;
  343. TkText *tkt = TKobj(TkText, tk);
  344. buf = mallocz(Tkmaxitem, 0);
  345. if(buf == nil)
  346. return TkNomem;
  347. *parg = tkword(tk->env->top, *parg, buf, buf+Tkmaxitem, nil);
  348. if(*buf == '\0') {
  349. free(buf);
  350. return TkOparg;
  351. }
  352. if(buf[0] >= '0' && buf[0] <= '9'){
  353. free(buf);
  354. return TkBadtg;
  355. }
  356. *ret = tktfindtag(tkt->tags, buf);
  357. if(*ret == nil) {
  358. e = tktaddtaginfo(tk, buf, ret);
  359. if(e != nil) {
  360. free(buf);
  361. return e;
  362. }
  363. }
  364. free(buf);
  365. return nil;
  366. }
  367. int
  368. tkttagnrange(TkText *tkt, int tid, TkTindex *i1, TkTindex *i2,
  369. TkTindex *istart, TkTindex *iend)
  370. {
  371. int found;
  372. found = 0;
  373. while(i1->line != &tkt->end) {
  374. if(i1->item == i2->item && i2->pos == 0)
  375. break;
  376. if(tkttagset(i1->item, tid)) {
  377. if(!found) {
  378. found = 1;
  379. *istart = *i1;
  380. }
  381. if(i1->item == i2->item) {
  382. /* i2->pos > 0 */
  383. *iend = *i2;
  384. return 1;
  385. }
  386. }
  387. else
  388. if(i1->item == i2->item || (found && i1->item->kind != TkTmark && i1->item->kind != TkTcontline))
  389. break;
  390. tktadjustind(tkt, TkTbyitem, i1);
  391. }
  392. if(found)
  393. *iend = *i1;
  394. return found;
  395. }
  396. static int
  397. tkttagprange(TkText *tkt, int tid, TkTindex *i1, TkTindex *i2,
  398. TkTindex *istart, TkTindex *iend)
  399. {
  400. int found;
  401. found = 0;
  402. while(i1->line != &tkt->start && i1->item != i2->item) {
  403. tktadjustind(tkt, TkTbyitemback, i1);
  404. if(tkttagset(i1->item, tid)) {
  405. if(!found) {
  406. found = 1;
  407. *iend = *i1;
  408. }
  409. }
  410. else
  411. if(found && i1->item->kind != TkTmark && i1->item->kind != TkTcontline)
  412. break;
  413. }
  414. if(found) {
  415. tktadjustind(tkt, TkTbyitem, i1);
  416. *istart = *i1;
  417. if(i1->item == i2->item)
  418. istart->pos = i2->pos;
  419. }
  420. return found;
  421. }
  422. /* XXX - Tad: potential memory leak on memory allocation failure */
  423. char *
  424. tkttagchange(Tk *tk, int tid, TkTindex *i1, TkTindex *i2, int add)
  425. {
  426. char *e;
  427. int samei, nextra, j, changed;
  428. TkTline *lmin, *lmax;
  429. TkTindex ixprev;
  430. TkTitem *nit;
  431. TkText *tkt = TKobj(TkText, tk);
  432. if(!tktindbefore(i1, i2))
  433. return nil;
  434. nextra = tid/32;
  435. lmin = nil;
  436. lmax = nil;
  437. tktadjustind(tkt, TkTbycharstart, i1);
  438. tktadjustind(tkt, TkTbycharstart, i2);
  439. samei = (i1->item == i2->item);
  440. if(i2->pos != 0) {
  441. e = tktsplititem(i2);
  442. if(e != nil)
  443. return e;
  444. if(samei) {
  445. /* split means i1 should now point to previous item */
  446. ixprev = *i2;
  447. tktadjustind(tkt, TkTbyitemback, &ixprev);
  448. i1->item = ixprev.item;
  449. }
  450. }
  451. if(i1->pos != 0) {
  452. e = tktsplititem(i1);
  453. if(e != nil)
  454. return e;
  455. }
  456. /* now i1 and i2 both point to beginning of non-mark/contline items */
  457. if(tid == TkTselid) {
  458. /*
  459. * Cache location of selection.
  460. * Note: there can be only one selection range in widget
  461. */
  462. if(add) {
  463. if(tkt->selfirst != nil)
  464. return TkBadsl;
  465. tkt->selfirst = i1->item;
  466. tkt->sellast = i2->item;
  467. }
  468. else {
  469. tkt->selfirst = nil;
  470. tkt->sellast = nil;
  471. }
  472. }
  473. while(i1->item != i2->item) {
  474. if(i1->item->kind != TkTmark && i1->item->kind != TkTcontline) {
  475. if(tid >= 32 && i1->item->tagextra < nextra) {
  476. nit = realloc(i1->item, sizeof(TkTitem) + nextra * sizeof(long));
  477. if(nit == nil)
  478. return TkNomem;
  479. for(j = nit->tagextra+1; j <= nextra; j++)
  480. nit->tags[j] = 0;
  481. nit->tagextra = nextra;
  482. if(i1->line->items == i1->item)
  483. i1->line->items = nit;
  484. else {
  485. ixprev = *i1;
  486. tktadjustind(tkt, TkTbyitemback, &ixprev);
  487. ixprev.item->next = nit;
  488. }
  489. /* check nit against cached items */
  490. if(tkt->selfirst == i1->item)
  491. tkt->selfirst = nit;
  492. if(tkt->sellast == i1->item)
  493. tkt->sellast = nit;
  494. i1->item = nit;
  495. }
  496. changed = tkttagbit(i1->item, tid, add);
  497. if(lmin == nil) {
  498. if(changed) {
  499. lmin = i1->line;
  500. lmax = lmin;
  501. }
  502. }
  503. else {
  504. if(changed)
  505. lmax = i1->line;
  506. }
  507. }
  508. if(!tktadjustind(tkt, TkTbyitem, i1))
  509. break;
  510. }
  511. if(lmin != nil) {
  512. tktfixgeom(tk, tktprevwrapline(tk, lmin), lmax, 0);
  513. tktextsize(tk, 1);
  514. }
  515. return nil;
  516. }
  517. static char*
  518. tkttagaddrem(Tk *tk, char *arg, int add)
  519. {
  520. char *e;
  521. TkText *tkt;
  522. TkTtaginfo *ti;
  523. TkTindex ix1, ix2;
  524. tkt = TKobj(TkText, tk);
  525. e = tkttagparse(tk, &arg, &ti);
  526. if(e != nil)
  527. return e;
  528. while(*arg != '\0') {
  529. e = tktindparse(tk, &arg, &ix1);
  530. if(e != nil)
  531. return e;
  532. if(*arg != '\0') {
  533. e = tktindparse(tk, &arg, &ix2);
  534. if(e != nil)
  535. return e;
  536. }
  537. else {
  538. ix2 = ix1;
  539. tktadjustind(tkt, TkTbychar, &ix2);
  540. }
  541. if(!tktindbefore(&ix1, &ix2))
  542. continue;
  543. e = tkttagchange(tk, ti->id, &ix1, &ix2, add);
  544. if(e != nil)
  545. return e;
  546. }
  547. return nil;
  548. }
  549. /* Text Tag Command (+ means implemented)
  550. +add
  551. +bind
  552. +cget
  553. +configure
  554. +delete
  555. +lower
  556. +names
  557. +nextrange
  558. +prevrange
  559. +raise
  560. +ranges
  561. +remove
  562. */
  563. static char*
  564. tkttagadd(Tk *tk, char *arg, char **val)
  565. {
  566. USED(val);
  567. return tkttagaddrem(tk, arg, 1);
  568. }
  569. static char*
  570. tkttagbind(Tk *tk, char *arg, char **val)
  571. {
  572. char *e;
  573. Rune r;
  574. TkTtaginfo *ti;
  575. TkAction *a;
  576. int event, mode;
  577. char *cmd, buf[Tkmaxitem];
  578. e = tkttagparse(tk, &arg, &ti);
  579. if(e != nil)
  580. return e;
  581. arg = tkskip(arg, " \t");
  582. if (arg[0] == '\0')
  583. return TkBadsq;
  584. arg = tkword(tk->env->top, arg, buf, buf+sizeof(buf), nil);
  585. if(buf[0] == '<') {
  586. event = tkseqparse(buf+1);
  587. if(event == -1)
  588. return TkBadsq;
  589. }
  590. else {
  591. chartorune(&r, buf);
  592. event = TkKey | r;
  593. }
  594. if(event == 0)
  595. return TkBadsq;
  596. arg = tkskip(arg, " \t");
  597. if(*arg == '\0') {
  598. for(a = ti->binds; a; a = a->link)
  599. if(event == a->event)
  600. return tkvalue(val, "%s", a->arg);
  601. return nil;
  602. }
  603. mode = TkArepl;
  604. if(*arg == '+') {
  605. mode = TkAadd;
  606. arg++;
  607. }
  608. else if(*arg == '-'){
  609. mode = TkAsub;
  610. arg++;
  611. }
  612. tkword(tk->env->top, arg, buf, buf+sizeof(buf), nil);
  613. cmd = strdup(buf);
  614. if(cmd == nil)
  615. return TkNomem;
  616. return tkaction(&ti->binds, event, TkDynamic, cmd, mode);
  617. }
  618. static char*
  619. tkttagcget(Tk *tk, char *arg, char **val)
  620. {
  621. char *e;
  622. TkTtaginfo *ti;
  623. TkOptab tko[3];
  624. e = tkttagparse(tk, &arg, &ti);
  625. if(e != nil)
  626. return e;
  627. tko[0].ptr = ti;
  628. tko[0].optab = tagopts;
  629. tko[1].ptr = ti;
  630. tko[1].optab = tagenvopts;
  631. tko[2].ptr = nil;
  632. return tkgencget(tko, arg, val, tk->env->top);
  633. }
  634. static char*
  635. tkttagconfigure(Tk *tk, char *arg, char **val)
  636. {
  637. char *e;
  638. TkOptab tko[3];
  639. TkTtaginfo *ti;
  640. TkTindex ix;
  641. TkText *tkt = TKobj(TkText, tk);
  642. USED(val);
  643. e = tkttagparse(tk, &arg, &ti);
  644. if(e != nil)
  645. return e;
  646. tko[0].ptr = ti;
  647. tko[0].optab = tagopts;
  648. tko[1].ptr = ti;
  649. tko[1].optab = tagenvopts;
  650. tko[2].ptr = nil;
  651. e = tkparse(tk->env->top, arg, tko, nil);
  652. if(e != nil)
  653. return e;
  654. if(tkttagind(tk, ti->name, 1, &ix)) {
  655. tktfixgeom(tk, tktprevwrapline(tk, ix.line), tkt->end.prev, 0);
  656. tktextsize(tk, 1);
  657. }
  658. return nil;
  659. }
  660. static void
  661. tktunlinktag(TkText *tkt, TkTtaginfo *t)
  662. {
  663. TkTtaginfo *f, **l;
  664. l = &tkt->tags;
  665. for(f = *l; f != nil; f = f->next) {
  666. if(f == t) {
  667. *l = t->next;
  668. return;
  669. }
  670. l = &f->next;
  671. }
  672. }
  673. static char*
  674. tkttagdelete(Tk *tk, char *arg, char **val)
  675. {
  676. TkText *tkt;
  677. TkTtaginfo *t;
  678. TkTindex ix;
  679. char *e;
  680. int found;
  681. USED(val);
  682. tkt = TKobj(TkText, tk);
  683. e = tkttagparse(tk, &arg, &t);
  684. if(e != nil)
  685. return e;
  686. found = 0;
  687. while(t != nil) {
  688. if(t->id == TkTselid)
  689. return TkBadvl;
  690. while(tkttagind(tk, t->name, 1, &ix)) {
  691. found = 1;
  692. tkttagbit(ix.item, t->id, 0);
  693. }
  694. tktunlinktag(tkt, t);
  695. t->next = nil;
  696. tktfreetags(t);
  697. if(*arg != '\0') {
  698. e = tkttagparse(tk, &arg, &t);
  699. if(e != nil)
  700. return e;
  701. }
  702. else
  703. t = nil;
  704. }
  705. if (found) {
  706. tktfixgeom(tk, &tkt->start, tkt->end.prev, 0);
  707. tktextsize(tk, 1);
  708. }
  709. return nil;
  710. }
  711. static char*
  712. tkttaglower(Tk *tk, char *arg, char **val)
  713. {
  714. TkText *tkt;
  715. TkTindex ix;
  716. TkTtaginfo *t, *tbelow, *f, **l;
  717. char *e;
  718. USED(val);
  719. tkt = TKobj(TkText, tk);
  720. e = tkttagparse(tk, &arg, &t);
  721. if(e != nil)
  722. return e;
  723. if(*arg != '\0') {
  724. e = tkttagparse(tk, &arg, &tbelow);
  725. if(e != nil)
  726. return e;
  727. }
  728. else
  729. tbelow = nil;
  730. tktunlinktag(tkt, t);
  731. if(tbelow != nil) {
  732. t->next = tbelow->next;
  733. tbelow->next = t;
  734. }
  735. else {
  736. l = &tkt->tags;
  737. for(f = *l; f != nil; f = f->next)
  738. l = &f->next;
  739. *l = t;
  740. t->next = nil;
  741. }
  742. if(tkttagind(tk, t->name, 1, &ix)) {
  743. tktfixgeom(tk, tktprevwrapline(tk, ix.line), tkt->end.prev, 0);
  744. tktextsize(tk, 1);
  745. }
  746. return nil;
  747. }
  748. static char*
  749. tkttagnames(Tk *tk, char *arg, char **val)
  750. {
  751. char *e, *r, *fmt;
  752. TkTtaginfo *t;
  753. TkTindex i;
  754. TkText *tkt = TKobj(TkText, tk);
  755. TkTitem *tagit;
  756. if(*arg != '\0') {
  757. e = tktindparse(tk, &arg, &i);
  758. if(e != nil)
  759. return e;
  760. /* make sure we're actually on a character */
  761. tktadjustind(tkt, TkTbycharstart, &i);
  762. tagit = i.item;
  763. }
  764. else
  765. tagit = nil;
  766. /* generate in order highest-to-lowest priority (contrary to spec) */
  767. fmt = "%s";
  768. for(t = tkt->tags; t != nil; t = t->next) {
  769. if(tagit == nil || tkttagset(tagit, t->id)) {
  770. r = tkvalue(val, fmt, t->name);
  771. if(r != nil)
  772. return r;
  773. fmt = " %s";
  774. }
  775. }
  776. return nil;
  777. }
  778. static char*
  779. tkttagnextrange(Tk *tk, char *arg, char **val)
  780. {
  781. char *e;
  782. TkTtaginfo *t;
  783. TkTindex i1, i2, istart, iend;
  784. TkText *tkt = TKobj(TkText, tk);
  785. e = tkttagparse(tk, &arg, &t);
  786. if(e != nil)
  787. return e;
  788. e = tktindparse(tk, &arg, &i1);
  789. if(e != nil)
  790. return e;
  791. if(*arg != '\0') {
  792. e = tktindparse(tk, &arg, &i2);
  793. if(e != nil)
  794. return e;
  795. }
  796. else
  797. tktendind(tkt, &i2);
  798. if(tkttagnrange(tkt, t->id, &i1, &i2, &istart, &iend))
  799. return tkvalue(val, "%d.%d %d.%d",
  800. tktlinenum(tkt, &istart), tktlinepos(tkt, &istart),
  801. tktlinenum(tkt, &iend), tktlinepos(tkt, &iend));
  802. return nil;
  803. }
  804. static char*
  805. tkttagprevrange(Tk *tk, char *arg, char **val)
  806. {
  807. char *e;
  808. TkTtaginfo *t;
  809. TkTindex i1, i2, istart, iend;
  810. TkText *tkt = TKobj(TkText, tk);
  811. e = tkttagparse(tk, &arg, &t);
  812. if(e != nil)
  813. return e;
  814. e = tktindparse(tk, &arg, &i1);
  815. if(e != nil)
  816. return e;
  817. if(*arg != '\0') {
  818. e = tktindparse(tk, &arg, &i2);
  819. if(e != nil)
  820. return e;
  821. }
  822. else
  823. tktstartind(tkt, &i2);
  824. if(tkttagprange(tkt, t->id, &i1, &i2, &istart, &iend))
  825. return tkvalue(val, "%d.%d %d.%d",
  826. tktlinenum(tkt, &istart), tktlinepos(tkt, &istart),
  827. tktlinenum(tkt, &iend), tktlinepos(tkt, &iend));
  828. return nil;
  829. }
  830. static char*
  831. tkttagraise(Tk *tk, char *arg, char **val)
  832. {
  833. TkText *tkt;
  834. TkTindex ix;
  835. TkTtaginfo *t, *tabove, *f, **l;
  836. char *e;
  837. USED(val);
  838. tkt = TKobj(TkText, tk);
  839. e = tkttagparse(tk, &arg, &t);
  840. if(e != nil)
  841. return e;
  842. if(*arg != '\0') {
  843. e = tkttagparse(tk, &arg, &tabove);
  844. if(e != nil)
  845. return e;
  846. }
  847. else
  848. tabove = nil;
  849. tktunlinktag(tkt, t);
  850. if(tabove != nil) {
  851. l = &tkt->tags;
  852. for(f = *l; f != nil; f = f->next) {
  853. if(f == tabove) {
  854. *l = t;
  855. t->next = tabove;
  856. break;
  857. }
  858. l = &f->next;
  859. }
  860. }
  861. else {
  862. t->next = tkt->tags;
  863. tkt->tags = t;
  864. }
  865. if(tkttagind(tk, t->name, 1, &ix)) {
  866. tktfixgeom(tk, tktprevwrapline(tk, ix.line), tkt->end.prev, 0);
  867. tktextsize(tk, 1);
  868. }
  869. return nil;
  870. }
  871. static char*
  872. tkttagranges(Tk *tk, char *arg, char **val)
  873. {
  874. char *e, *fmt;
  875. TkTtaginfo *t;
  876. TkTindex i1, i2, istart, iend;
  877. TkText *tkt = TKobj(TkText, tk);
  878. e = tkttagparse(tk, &arg, &t);
  879. if(e != nil)
  880. return e;
  881. tktstartind(tkt, &i1);
  882. tktendind(tkt, &i2);
  883. fmt = "%d.%d %d.%d";
  884. while(tkttagnrange(tkt, t->id, &i1, &i2, &istart, &iend)) {
  885. e = tkvalue(val, fmt,
  886. tktlinenum(tkt, &istart), tktlinepos(tkt, &istart),
  887. tktlinenum(tkt, &iend), tktlinepos(tkt, &iend));
  888. if(e != nil)
  889. return e;
  890. fmt = " %d.%d %d.%d";
  891. i1 = iend;
  892. }
  893. return nil;
  894. }
  895. static char*
  896. tkttagremove(Tk *tk, char *arg, char **val)
  897. {
  898. USED(val);
  899. return tkttagaddrem(tk, arg, 0);
  900. }