1: /* Copyright (c) Stichting Mathematisch Centrum, Amsterdam, 1984. */ 2: static char rcsid[] = "$Header: edit.c,v 2.5 85/08/22 16:01:43 timo Exp $"; 3: 4: /* 5: * B editor -- Read unit from file. 6: */ 7: 8: #include <ctype.h> 9: 10: #include "b.h" 11: #include "feat.h" 12: #include "erro.h" 13: #include "bobj.h" 14: #include "node.h" 15: #include "tabl.h" 16: #include "gram.h" 17: #include "supr.h" 18: #include "queu.h" 19: 20: string unixerror(); 21: 22: /* 23: * TABSIZE sets the number of spaces equivalent to a tab character 24: * read from the input; INDENT sets the number of spaces for one indentation 25: * level. 26: * The definitions here are unrelated to the definition of TABS 27: * in eval.h (used by show.c and eval.c). The definition here only 28: * defines how many spaces must be equivalenced to a tab stop when read 29: * from a file; tab stops must be caused by editing a unit with another 30: * editor (vi, ed, ex, emacs), since "save.c" always writes spaces, 31: * not tabs. The value '4' is best suited for people at the CWI who 32: * may have workspaces with units edited with the previous version of 33: * the B editor, which emitted a tab for each indentation level (and 34: * assumed 4 spaces for a tab stop on input). 35: * 36: * The variables 'spacesused' and 'tabsused' are kept to see if mixed use 37: * of spaces and tabs was made; this can cause indentation errors. 38: */ 39: 40: #ifdef CWI 41: #define TABSIZE 4 42: #else 43: #define TABSIZE 8 44: #endif 45: 46: #define INDENT 4 47: 48: Hidden bool spacesused; 49: Hidden bool tabsused; 50: 51: 52: /* 53: * Read (edit) parse tree from file into the focus. 54: * Rather ad hoc, we use ins_string for each line 55: * and do some magic tricks to get the indentation right 56: * (most of the time). 57: * If line > 0, position the focus at that line, if possible; 58: * otherwise the focus is left at the end of the inserted text. 59: */ 60: 61: Visible bool 62: edit(ep, filename, line) 63: register environ *ep; 64: string filename; 65: int line; 66: { 67: int lines = 0; 68: register FILE *fp = fopen(filename, "r"); 69: register int c; 70: char buf[BUFSIZ]; 71: auto string cp; 72: auto queue q = Qnil; 73: 74: if (!fp) { 75: error("%s", unixerror(filename)); 76: return No; 77: } 78: 79: spacesused = tabsused = No; 80: do { 81: do { 82: for (cp = buf; cp < buf + sizeof buf - 1; ++cp) { 83: c = getc(fp); 84: if (c == EOF || c == '\n') 85: break; 86: if (c < ' ' || c >= 0177) 87: c = ' '; 88: *cp = c; 89: } 90: if (cp > buf) { 91: *cp = 0; 92: if (!ins_string(ep, buf, &q, 0) || !emptyqueue(q)) { 93: qrelease(q); 94: error(EDIT_BAD); 95: fclose(fp); 96: return No; 97: } 98: qrelease(q); 99: } 100: } while (c != EOF && c != '\n'); 101: ++lines; 102: if (c != EOF && !editindentation(ep, fp)) { 103: fclose(fp); 104: return No; 105: } 106: } while (c != EOF); 107: fclose(fp); 108: if (ep->mode == FHOLE || ep->mode == VHOLE && (ep->s1&1)) { 109: cp = ""; 110: soften(ep, &cp, 0); 111: } 112: if (lines > 1 && line > 0) { 113: gotoyx(ep, line-1, 0); 114: oneline(ep); 115: } 116: if (spacesused && tabsused) 117: error(EDIT_TABS); 118: return Yes; 119: } 120: 121: 122: /* 123: * Do all the footwork required to get the indentation proper. 124: */ 125: 126: Hidden Procedure 127: editindentation(ep, fp) 128: register environ *ep; 129: register FILE *fp; 130: { 131: register int tabs = 0; 132: auto int level; 133: register int c; 134: 135: while ((c = getc(fp)) == ' ' || c == '\t') { 136: if (c == ' ') { 137: spacesused = Yes; 138: ++tabs; 139: } 140: else { 141: tabsused = Yes; 142: tabs = (tabs/TABSIZE + 1)*TABSIZE; 143: } 144: } 145: ungetc(c, fp); 146: if (c == EOF || c == '\n') 147: return Yes; 148: tabs = (tabs+(INDENT/2))/INDENT; /* Transform to tab stops */ 149: if (!ins_newline(ep)) { 150: #ifndef NDEBUG 151: debug("[Burp! Can't insert a newline.]"); 152: #endif NDEBUG 153: return No; 154: } 155: level = Level(ep->focus); 156: for (; tabs < level; --level) { 157: if (!ins_newline(ep)) { 158: #ifndef NDEBUG 159: debug("[Burp, burp! Can't decrease indentation.]"); 160: #endif NDEBUG 161: return No; 162: } 163: } 164: fixit(ep); 165: return Yes; 166: } 167: 168: 169: /* ------------------------------------------------------------ */ 170: 171: #ifdef SAVEBUF 172: 173: /* 174: * Read the next non-space character. 175: */ 176: 177: Hidden int 178: skipsp(fp) 179: register FILE *fp; 180: { 181: register int c; 182: 183: do { 184: c = getc(fp); 185: } while (c == ' '); 186: return c; 187: } 188: 189: 190: /* 191: * Read a text in standard B format when the initial quote has already 192: * been read. 193: */ 194: 195: Hidden value 196: readtext(fp, quote) 197: register FILE *fp; 198: register char quote; 199: { 200: auto value v = Vnil; 201: char buf[BUFSIZ]; 202: register string cp = buf; 203: register int c; 204: auto int i; 205: 206: for (; ; ++cp) { 207: c = getc(fp); 208: if (!isascii(c) || c != ' ' && !isprint(c)) { 209: if (c == EOF) 210: debug("readtext: EOF"); 211: else 212: debug("readtext: bad char (0%02o)", c); 213: release(v); 214: return Vnil; /* Bad character or EOF */ 215: } 216: if (c == quote) { 217: c = getc(fp); 218: if (c != quote) { 219: ungetc(c, fp); 220: break; 221: } 222: } 223: else if (c == '`') { 224: c = skipsp(fp); 225: if (c == '$') { 226: i = 0; 227: if (fscanf(fp, "%d", &i) != 1 228: || i == 0 || !isascii(i)) { 229: debug("readtext: error in conversion"); 230: release(v); 231: return Vnil; 232: } 233: c = skipsp(fp); 234: } 235: else 236: i = '`'; 237: if (c != '`') { 238: if (c == EOF) 239: debug("readtext: EOF in conversion"); 240: else 241: debug("readtext: bad char in conversion (0%o)", c); 242: release(v); 243: return Vnil; 244: } 245: c = i; 246: } 247: if (cp >= &buf[sizeof buf - 1]) { 248: *cp = 0; 249: if (v) 250: concato(&v, buf); 251: else 252: v = mk_text(buf); 253: cp = buf; 254: } 255: *cp = c; 256: } 257: *cp = 0; 258: if (!v) 259: return mk_text(buf); 260: concato(&v, buf); 261: return v; 262: } 263: 264: 265: Hidden int 266: readsym(fp) 267: register FILE *fp; 268: { 269: register int c; 270: char buf[100]; 271: register string bufp; 272: 273: for (bufp = buf; ; ++bufp) { 274: c = getc(fp); 275: if (c == EOF) 276: return -1; 277: if (!isascii(c) || !isalnum(c) && c != '_') { 278: if (ungetc(c, fp) == EOF) 279: syserr("readsym: ungetc failed"); 280: break; 281: } 282: *bufp = c; 283: } 284: *bufp = 0; 285: if (isdigit(buf[0])) 286: return atoi(buf); 287: if (strcmp(buf, "Required") == 0) /***** Compatibility hack *****/ 288: return Hole; 289: return nametosym(buf); 290: } 291: 292: 293: /* 294: * Read a node in internal format (recursively). 295: * Return nil pointer if EOF or error. 296: */ 297: 298: Hidden node 299: readnode(fp) 300: FILE *fp; 301: { 302: int c; 303: int nch; 304: node ch[MAXCHILD]; 305: node n; 306: int sym; 307: 308: c = skipsp(fp); 309: switch (c) { 310: case EOF: 311: return Nnil; /* EOF hit */ 312: 313: case '(': 314: sym = readsym(fp); 315: if (sym < 0) { 316: debug("readnode: missing symbol"); 317: return Nnil; /* No number as first item */ 318: } 319: if (sym < 0 || sym > Hole) { 320: debug("readnode: bad symbol (%d)", sym); 321: return Nnil; 322: } 323: nch = 0; 324: while ((c = skipsp(fp)) == ',' && nch < MAXCHILD) { 325: n = readnode(fp); 326: if (!n) { 327: for (; nch > 0; --nch) 328: noderelease(ch[nch-1]); 329: return Nnil; /* Error encountered in child */ 330: } 331: ch[nch] = n; 332: ++nch; 333: } 334: if (c != ')') { 335: if (c == ',') 336: debug("readnode: node too long (sym=%d)", sym); 337: else 338: debug("readnode: no ')' where expected (sym=%d)", sym); 339: for (; nch > 0; --nch) 340: noderelease(ch[nch-1]); 341: return Nnil; /* Not terminated with ')' or too many children */ 342: } 343: if (nch == 0) 344: return gram(sym); /* Saves space for Optional/Hole nodes */ 345: return newnode(nch, sym, ch); 346: 347: case '\'': 348: case '"': 349: return (node) readtext(fp, c); 350: 351: default: 352: debug("readnode: bad initial character"); 353: return Nnil; /* Bad initial character */ 354: } 355: } 356: 357: 358: /* 359: * Read a node written in a more or less internal format. 360: */ 361: 362: Visible value 363: editqueue(filename) 364: string filename; 365: { 366: register FILE *fp = fopen(filename, "r"); 367: auto queue q = Qnil; 368: register node n; 369: 370: if (!fp) 371: return Vnil; 372: do { 373: n = readnode(fp); 374: if (!n) 375: break; /* EOF or error */ 376: addtoqueue(&q, n); 377: noderelease(n); 378: } while (skipsp(fp) == '\n'); 379: fclose(fp); 380: return (value)q; 381: } 382: #endif SAVEBUF