1: /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1984. */ 2: static char rcsid[] = "$Header: sugg.c,v 2.4 84/10/26 12:10:51 guido Exp $"; 3: 4: /* 5: * B editor -- New suggestion handling module. 6: */ 7: 8: #include "feat.h" 9: 10: #ifdef USERSUGG 11: 12: #include "b.h" 13: #include "bobj.h" 14: #include "node.h" 15: #include "supr.h" 16: #include "gram.h" 17: #include "queu.h" 18: 19: #include <ctype.h> 20: 21: extern bool dflag; 22: extern bool edontop; 23: 24: extern bool lefttorite; 25: 26: #ifndef SUGGFILE 27: #define SUGGFILE ".Bed_sugg" 28: #endif 29: 30: #define MAXNSUGG 1000 31: 32: Hidden value sugg[MAXNSUGG]; 33: Hidden int nsugg; 34: Hidden int nbuiltin; 35: Hidden bool suggchanges; 36: Hidden bool ignorefirstcall; /* Communication between killsugg and setsugg */ 37: 38: /* 39: * Read the suggestion table from file. 40: */ 41: 42: Visible Procedure 43: initsugg() 44: { 45: char buffer[1000]; 46: register FILE *fp; 47: register c; 48: 49: fp = fopen(SUGGFILE, "r"); 50: if (!fp) { 51: if (dflag) { 52: fprintf(stderr, "*** No suggestion file: "); 53: perror(SUGGFILE); 54: } 55: return; 56: } 57: while (fgets(buffer, sizeof buffer, fp)) { 58: if (!index(buffer, '\n')) { /* Skip long line */ 59: fprintf(stderr, 60: "*** Excessively long suggestion ignored\n"); 61: while ((c = getc(fp)) != '\n' && c != EOF) 62: ; 63: } 64: else 65: addsugg(buffer, -1); 66: } 67: fclose(fp); 68: } 69: 70: 71: /* 72: * Make sure a line looks like a suggestion, return No if not. 73: * Replace the trailing newline or comment-sign by a zero byte. 74: * ***** Should check more thoroughly. ***** 75: */ 76: 77: Hidden bool 78: checksugg(bp) 79: string bp; 80: { 81: if (!isascii(*bp) || !isupper(*bp)) 82: return No; 83: while (*bp && *bp != '\n' && *bp != '\\') 84: ++bp; 85: *bp = 0; 86: return Yes; 87: } 88: 89: 90: /* 91: * Procedure to add a suggestion to the suggestion table. 92: */ 93: 94: Visible Procedure 95: addsugg(str, builtin) 96: string str; 97: int builtin; 98: { 99: int i; 100: int j; 101: int len; 102: int cmp; 103: string suggi; 104: int where = (builtin == -1) ? nsugg : nbuiltin; 105: 106: if (!checksugg(str)) 107: return; 108: for (len = 0; str[len] && str[len] != ' '; ++len) 109: ; 110: for (i = nsugg-1; i >= 0; --i) { 111: suggi = Str(sugg[i]); 112: cmp = strncmp(str, suggi, len); 113: if (cmp < 0) 114: continue; 115: if (cmp > 0) { 116: if (i >= where) 117: where = i+1; 118: continue; 119: } 120: if (suggi[len] && suggi[len] != ' ') 121: continue; /* No match, just prefix */ 122: if (Strequ(str+len, suggi+len)) 123: return; /* Ignore exact duplicates */ 124: if (i < nbuiltin) 125: return; /* Cannot replace built-in */ 126: /* Replacement */ 127: sugg[i] = mk_text(str); /* No use to release the old one... */ 128: /* ...its refcount is infinite */ 129: fix(sugg[i]); 130: suggchanges = Yes; 131: return; 132: } 133: /* Insertion */ 134: if (nsugg >= MAXNSUGG) 135: return; /* Table overflow */ 136: if (builtin == Yes) 137: ++nbuiltin; 138: for (j = nsugg; j > where; --j) 139: sugg[j] = sugg[j-1]; 140: ++nsugg; 141: sugg[where] = mk_text(str); 142: fix(sugg[where]); 143: suggchanges = Yes; 144: } 145: 146: 147: /* 148: * Procedure to delete a suggestion from the suggestion table. 149: * Must supply the whole string as argument. 150: */ 151: 152: Hidden Procedure 153: delsugg(str) 154: string str; 155: { 156: int i; 157: 158: for (i = 0; i < nsugg; ++i) { 159: if (Strequ(str, Str(sugg[i]))) { 160: --nsugg; 161: for (; i < nsugg; ++i) 162: sugg[i] = sugg[i+1]; 163: suggchanges = Yes; 164: return; 165: } 166: } 167: } 168: 169: 170: /* 171: * Return a suitable suggestion which matches str for len characters. 172: * If len > 1, and all of str (even beyond len) equals some table 173: * entry, the first matching entry after that is preferred; otherwise, 174: * the first matching entry at all is returned. 175: * Vnil is returned if no entry matches. 176: */ 177: 178: Hidden node 179: nextsugg(str, len) 180: string str; 181: int len; 182: { 183: bool found = !str[len]; 184: int first = -1; 185: int i; 186: 187: for (i = 0; i < nsugg; ++i) { 188: if (!Strnequ(str, Str(sugg[i]), len)) 189: continue; 190: if (found) 191: return (node) sugg[i]; 192: if (Strequ(str+len, Str(sugg[i])+len)) 193: found = Yes; 194: if (first < 0) 195: first = i; 196: } 197: if (first >= 0) 198: return (node) sugg[first]; 199: return Nnil; 200: } 201: 202: 203: /* 204: * Procedure to save the suggestion file if it has been changed. 205: */ 206: 207: Visible Procedure 208: endsugg() 209: { 210: FILE *fp; 211: int i; 212: 213: if (!suggchanges) 214: return; 215: suggchanges = No; 216: fp = fopen(SUGGFILE, "w"); 217: if (!fp) { 218: if (dflag) { 219: fprintf(stderr, "*** Can't rewrite "); 220: perror(SUGGFILE); 221: } 222: return; 223: } 224: if (dflag) 225: fprintf(stderr, "*** [Rewriting suggestion file]\n"); 226: for (i = nbuiltin; i < nsugg; ++i) 227: fprintf(fp, "%s\n", Str(sugg[i])); 228: if (fclose(fp) == EOF) { 229: fprintf(stderr, "*** Can't finish writing "); 230: perror(SUGGFILE); 231: return; 232: } 233: } 234: 235: 236: /* 237: * Find a new suggestion or advance in the current one. 238: * Interface styled like resuggest: string pointer is advanced here. 239: */ 240: 241: Visible bool 242: newsugg(ep, pstr, alt_c) 243: environ *ep; 244: string *pstr; 245: int alt_c; 246: { 247: char buffer[1000]; 248: node n = tree(ep->focus); 249: node nn; 250: int sym = symbol(n); 251: string str; 252: string bp; 253: bool end; 254: 255: Assert(pstr && *pstr); 256: if (sym != Suggestion || ep->mode != VHOLE || ep->s1 != 2) 257: return No; 258: strncpy(buffer, Str((value)firstchild(n)), sizeof buffer); 259: for (str = *pstr, bp = buffer+ep->s2, end = No; 260: *str && bp < buffer + sizeof buffer; ++str, ++bp) { 261: if (!*bp) 262: end = Yes; 263: *bp = *str; 264: } 265: if (end) 266: *bp = 0; 267: nn = (node)nextsugg(buffer, ep->s2 + 1); 268: if (!nn) { 269: if (!alt_c) 270: return No; 271: buffer[ep->s2] = alt_c; 272: nn = (node)nextsugg(buffer, ep->s2 + 1); 273: if (!nn) 274: return No; 275: } 276: if (nn != firstchild(n)) { 277: s_down(ep); 278: replace(&ep->focus, nn); 279: s_up(ep); 280: } 281: /* No need to release because its refcount is infinite anyway */ 282: ++ep->s2; 283: if (**pstr == ' ') 284: accsugg(ep); 285: ++*pstr; 286: return Yes; 287: } 288: 289: 290: /* 291: * Kill suggestion -- only the part to the left of the focus is kept. 292: */ 293: 294: Visible Procedure 295: killsugg(ep) 296: environ *ep; 297: { 298: queue q = Qnil; 299: char buffer[1000]; 300: node n = tree(ep->focus); 301: 302: Assert(ep->mode == VHOLE && ep->s1 == 2 && symbol(n) == Suggestion); 303: strncpy(buffer, Str((value)firstchild(n)), ep->s2); 304: buffer[ep->s2] = 0; 305: delfocus(&ep->focus); 306: ep->mode = WHOLE; 307: ignorefirstcall = Yes; 308: ins_string(ep, buffer, &q, 0); 309: qrelease(q); 310: ignorefirstcall = No; 311: } 312: 313: 314: /* 315: * Place an initial suggestion in a node. 316: */ 317: 318: Visible bool 319: setsugg(pp, c, ep) 320: path *pp; 321: char c; 322: environ *ep; 323: { 324: char buf[2]; 325: node n; 326: 327: if (lefttorite) 328: return No; 329: if (ignorefirstcall) { 330: ignorefirstcall = No; 331: return No; 332: } 333: buf[0] = c; 334: buf[1] = 0; 335: n = (node)nextsugg(buf, 1); 336: if (!n) 337: return No; 338: replace(pp, newnode(1, Suggestion, &n)); 339: ep->mode = VHOLE; 340: ep->s1 = 2; 341: ep->s2 = 1; 342: return Yes; 343: } 344: 345: 346: /* 347: * Accept a suggestion -- turn it into real nodes. 348: */ 349: 350: Visible Procedure 351: accsugg(ep) 352: environ *ep; 353: { 354: node n = tree(ep->focus); 355: int s2 = ep->s2; 356: queue q = Qnil; 357: environ env; 358: 359: Assert(symbol(n) == Suggestion && ep->mode == VHOLE && ep->s1 == 2); 360: stringtoqueue(Str((value)firstchild(n)) + s2, &q); 361: killsugg(ep); 362: Ecopy(*ep, env); 363: if (app_queue(ep, &q)) 364: Erelease(env); 365: else { 366: Erelease(*ep); 367: Emove(env, *ep); 368: qrelease(q); 369: } 370: } 371: 372: 373: /* 374: * Procedure called when a unit is read in. 375: * It tries to update the suggestion database. 376: * It also remembers the suggestion so that it can be removed by writesugg 377: * if that finds the unit was deleted. 378: */ 379: 380: Hidden char lastsugg[1000]; 381: 382: Visible Procedure 383: readsugg(p) 384: path p; 385: { 386: p = pathcopy(p); 387: top(&p); 388: getpattern(lastsugg, tree(p)); 389: pathrelease(p); 390: addsugg(lastsugg, No); 391: } 392: 393: 394: /* 395: * Procedure called when a unit is saved. 396: * It tries to update the suggestion database. 397: * If the unit appears empty, the last suggestion passed to readsugg 398: * will be deleted. 399: */ 400: 401: Visible Procedure 402: writesugg(p) 403: path p; 404: { 405: p = pathcopy(p); 406: top(&p); 407: if (width(tree(p)) == 0) 408: delsugg(lastsugg); 409: else { 410: getpattern(lastsugg, tree(p)); 411: if (lastsugg[0]) 412: addsugg(lastsugg, No); 413: } 414: pathrelease(p); 415: } 416: 417: 418: /* 419: * Procedure to find out the suggestion that fits the current unit. 420: * Makes the buffer empty if not a HOW'TO unit. 421: * ***** Won't work if B-grammar is severely changed! ***** 422: */ 423: 424: Hidden Procedure 425: getpattern(buffer, n) 426: string buffer; 427: node n; 428: { 429: string *rp = noderepr(n); 430: 431: buffer[0] = 0; 432: while (Fw_zero(rp[0])) { 433: if (nchildren(n) == 0) 434: return; 435: n = firstchild(n); 436: rp = noderepr(n); 437: } 438: if (!Strequ(rp[0], "HOW'TO ") || nchildren(n) < 1) 439: return; 440: subgetpattern(&buffer, firstchild(n)); 441: *buffer = 0; 442: } 443: 444: 445: /* 446: * Refinement for getpattern to do the work. 447: */ 448: 449: Hidden Procedure 450: subgetpattern(pbuf, n) 451: string *pbuf; 452: node n; 453: { 454: string *rp; 455: int i; 456: int nch; 457: 458: rp = noderepr(n); 459: nch = (Type(n) == Tex) ? 0 : nchildren(n); 460: for (i = 0; i <= nch; ++i) { 461: if (i > 0) 462: subgetpattern(pbuf, child(n, i)); 463: if (Fw_positive(rp[i])) { 464: if (islower(rp[i][0])) 465: *(*pbuf)++ = '?'; 466: else { 467: strcpy(*pbuf, rp[i]); 468: *pbuf += strlen(*pbuf); 469: } 470: } 471: } 472: } 473: 474: #endif USERSUGG