1: /************************************************************************* 2: * This program is copyright (C) 1985, 1986 by Jonathan Payne. It is * 3: * provided to you without charge for use only on a licensed Unix * 4: * system. You may copy JOVE provided that this notice is included with * 5: * the copy. You may not sell copies of this program or versions * 6: * modified for use on microcomputer systems, unless the copies are * 7: * included with a Unix system distribution and the source is provided. * 8: *************************************************************************/ 9: 10: #include "jove.h" 11: #include "ctype.h" 12: #include <signal.h> 13: 14: struct cmd * 15: FindCmd(proc) 16: register int (*proc)(); 17: { 18: register struct cmd *cp; 19: 20: for (cp = commands; cp->Name; cp++) 21: if (cp->c_proc == proc) 22: return cp; 23: return 0; 24: } 25: 26: int Interactive; /* True when we invoke with the command handler? */ 27: char *ProcFmt = ": %f "; 28: 29: ExecCmd(cp) 30: data_obj *cp; 31: { 32: LastCmd = cp; 33: if (cp->Type & MAJOR_MODE) 34: SetMajor((cp->Type >> 8)); 35: else if (cp->Type & MINOR_MODE) 36: TogMinor((cp->Type >> 8)); 37: else switch (cp->Type&TYPEMASK) { 38: case MACRO: 39: do_macro((struct macro *) cp); 40: break; 41: 42: case FUNCTION: 43: { 44: struct cmd *cmd = (struct cmd *) cp; 45: 46: if (cmd->c_proc) 47: (*cmd->c_proc)(); 48: } 49: } 50: } 51: 52: Line * 53: lastline(lp) 54: register Line *lp; 55: { 56: while (lp->l_next) 57: lp = lp->l_next; 58: return lp; 59: } 60: 61: Upper(c) 62: register int c; 63: { 64: return (islower(c) ? toupper(c) : c); 65: } 66: 67: int alarmed = 0; 68: 69: char key_strokes[100]; 70: static char *key_p = key_strokes; 71: 72: init_strokes() 73: { 74: key_strokes[0] = 0; 75: key_p = key_strokes; 76: } 77: 78: add_stroke(c) 79: { 80: if (key_p + 5 > &key_strokes[(sizeof key_strokes) - 1]) 81: key_p = key_strokes; 82: sprintf(key_p, "%p ", c); 83: key_p += strlen(key_p); 84: } 85: 86: slowpoke() 87: { 88: alarmed++; 89: f_mess(key_strokes); 90: } 91: 92: #ifdef BSD4_2 93: # define N_SEC 1 /* will be precisely 1 second on 4.2 */ 94: #else 95: # define N_SEC 2 /* but from 0 to 2 seconds otherwise */ 96: #endif 97: 98: waitchar() 99: { 100: #ifdef EUNICE 101: return getch(); 102: #endif 103: unsigned int old_time; 104: int c; 105: int (*oldproc)(); 106: 107: alarmed = 0; 108: oldproc = signal(SIGALRM, slowpoke); 109: 110: if ((old_time = alarm((unsigned) N_SEC)) == 0) 111: old_time = UpdFreq; 112: c = getch(); 113: (void) alarm(old_time); 114: (void) signal(SIGALRM, oldproc); 115: 116: return c; 117: } 118: 119: /* dir > 0 means forward; else means backward. */ 120: 121: char * 122: StrIndex(dir, buf, charpos, what) 123: char *buf, 124: what; 125: { 126: char *cp = &buf[charpos], 127: c; 128: 129: if (dir > 0) { 130: while (c = *cp++) 131: if (c == what) 132: return (cp - 1); 133: } else { 134: while (cp >= buf && (c = *cp--)) 135: if (c == what) 136: return (cp + 1); 137: } 138: return 0; 139: } 140: 141: blnkp(buf) 142: register char *buf; 143: { 144: register char c; 145: 146: while ((c = *buf++) && (c == ' ' || c == '\t')) 147: ; 148: return c == 0; /* It's zero if we got to the end of the Line */ 149: } 150: 151: Line * 152: next_line(line, num) 153: register Line *line; 154: register int num; 155: { 156: if (num < 0) 157: return prev_line(line, -num); 158: if (line) 159: while (--num >= 0 && line->l_next != 0) 160: line = line->l_next; 161: return line; 162: } 163: 164: Line * 165: prev_line(line, num) 166: register Line *line; 167: register int num; 168: { 169: if (num < 0) 170: return next_line(line, -num); 171: if (line) 172: while (--num >= 0 && line->l_prev != 0) 173: line = line->l_prev; 174: return line; 175: } 176: 177: DotTo(line, col) 178: Line *line; 179: { 180: Bufpos bp; 181: 182: bp.p_line = line; 183: bp.p_char = col; 184: SetDot(&bp); 185: } 186: 187: /* If bp->p_line is != current line, then save current line. Then set dot 188: to bp->p_line, and if they weren't equal get that line into linebuf. */ 189: 190: SetDot(bp) 191: register Bufpos *bp; 192: { 193: register int notequal; 194: 195: if (bp == 0) 196: return; 197: 198: notequal = bp->p_line != curline; 199: if (notequal) 200: lsave(); 201: if (bp->p_line) 202: curline = bp->p_line; 203: curchar = bp->p_char; 204: if (notequal) 205: getDOT(); 206: } 207: 208: ToLast() 209: { 210: SetLine(curbuf->b_last); 211: Eol(); 212: } 213: 214: int MarkThresh = 22; /* Average screen size ... */ 215: static int line_diff; 216: 217: LineDist(nextp, endp) 218: register Line *nextp, 219: *endp; 220: { 221: (void) inorder(nextp, 0, endp, 0); 222: return line_diff; 223: } 224: 225: inorder(nextp, char1, endp, char2) 226: register Line *nextp, 227: *endp; 228: { 229: int count = 0; 230: register Line *prevp = nextp; 231: 232: line_diff = 0; 233: if (nextp == endp) 234: return char1 < char2; 235: 236: while (nextp || prevp) { 237: if (nextp == endp || prevp == endp) 238: break; 239: if (nextp) 240: nextp = nextp->l_next; 241: if (prevp) 242: prevp = prevp->l_prev; 243: count++; 244: } 245: if (nextp == 0 && prevp == 0) 246: return -1; 247: line_diff = count; 248: 249: return nextp == endp; 250: } 251: 252: PushPntp(line) 253: register Line *line; 254: { 255: exp_p = 0; 256: if (LineDist(curline, line) >= MarkThresh) 257: SetMark(); 258: } 259: 260: ToFirst() 261: { 262: SetLine(curbuf->b_first); 263: } 264: 265: length(line) 266: Line *line; 267: { 268: return strlen(lcontents(line)); 269: }; 270: 271: to_word(dir) 272: register int dir; 273: { 274: register char c; 275: 276: if (dir > 0) { 277: while ((c = linebuf[curchar]) != 0 && !isword(c)) 278: curchar++; 279: if (eolp()) { 280: if (curline->l_next == 0) 281: return; 282: SetLine(curline->l_next); 283: to_word(dir); 284: return; 285: } 286: } else { 287: while (!bolp() && (c = linebuf[curchar - 1], !isword(c))) 288: --curchar; 289: if (bolp()) { 290: if (curline->l_prev == 0) 291: return; 292: SetLine(curline->l_prev); 293: Eol(); 294: to_word(dir); 295: } 296: } 297: } 298: 299: /* Are there any modified buffers? Allp means include B_PROCESS and 300: B_IPROCESS buffers in the check, but never scratch buffers. */ 301: 302: ModBufs(allp) 303: { 304: register Buffer *b; 305: 306: for (b = world; b != 0; b = b->b_next) { 307: if (b->b_type == B_SCRATCH) 308: continue; 309: if ((b->b_type == B_FILE || allp) && IsModified(b)) 310: return 1; 311: } 312: return 0; 313: } 314: 315: char * 316: filename(b) 317: register Buffer *b; 318: { 319: return b->b_fname ? pr_name(b->b_fname) : "[No file]"; 320: } 321: 322: char * 323: itoa(num) 324: register int num; 325: { 326: static char line[15]; 327: 328: sprintf(line, "%d", num); 329: return line; 330: } 331: 332: min(a, b) 333: register int a, 334: b; 335: { 336: return (a < b) ? a : b; 337: } 338: 339: max(a, b) 340: register int a, 341: b; 342: { 343: return (a > b) ? a : b; 344: } 345: 346: tiewind(wp, bp) 347: register Window *wp; 348: register Buffer *bp; 349: { 350: UpdModLine++; /* Kludge ... but speeds things up considerably */ 351: wp->w_line = bp->b_dot; 352: wp->w_char = bp->b_char; 353: wp->w_bufp = bp; 354: } 355: 356: extern int Jr_Len; 357: 358: char * 359: lcontents(line) 360: register Line *line; 361: { 362: if (line == curline) 363: return linebuf; 364: else 365: return lbptr(line); 366: } 367: 368: char * 369: ltobuf(line, buf) 370: Line *line; 371: char *buf; 372: { 373: if (line == curline) { 374: if (buf != linebuf) 375: strcpy(buf, linebuf); 376: Jr_Len = strlen(linebuf); 377: } else 378: (void) getline(line->l_dline, buf); 379: return buf; 380: } 381: 382: /* Return none-zero if we had to rearrange the order. */ 383: 384: fixorder(line1, char1, line2, char2) 385: register Line **line1, 386: **line2; 387: register int *char1, 388: *char2; 389: { 390: Line *tline; 391: int tchar; 392: 393: if (inorder(*line1, *char1, *line2, *char2)) 394: return 0; 395: 396: tline = *line1; 397: tchar = *char1; 398: *line1 = *line2; 399: *char1 = *char2; 400: *line2 = tline; 401: *char2 = tchar; 402: 403: return 1; 404: } 405: 406: inlist(first, what) 407: register Line *first, 408: *what; 409: { 410: while (first) { 411: if (first == what) 412: return 1; 413: first = first->l_next; 414: } 415: return 0; 416: } 417: 418: /* Make `buf' modified and tell the redisplay code to update the modeline 419: if it will need to be changed. */ 420: 421: int ModCount = 0; 422: 423: modify() 424: { 425: extern int DOLsave; 426: 427: if (!curbuf->b_modified) 428: UpdModLine++; 429: curbuf->b_modified++; 430: DOLsave++; 431: if (!Asking) 432: ModCount++; 433: } 434: 435: unmodify() 436: { 437: if (curbuf->b_modified) 438: UpdModLine++; 439: curbuf->b_modified = 0; 440: } 441: 442: numcomp(s1, s2) 443: register char *s1, 444: *s2; 445: { 446: register int count = 0; 447: 448: while (*s1 != 0 && *s1++ == *s2++) 449: count++; 450: return count; 451: } 452: 453: char * 454: copystr(str) 455: char *str; 456: { 457: char *val = emalloc(strlen(str) + 1); 458: 459: strcpy(val, str); 460: return val; 461: } 462: 463: #ifndef byte_copy 464: byte_copy(from, to, count) 465: register char *from, 466: *to; 467: register int count; 468: { 469: while (--count >= 0) 470: *to++ = *from++; 471: } 472: #endif 473: 474: len_error(flag) 475: { 476: char *mesg = "[line too long]"; 477: 478: (flag == COMPLAIN) ? complain(mesg) : error(mesg); 479: } 480: 481: /* Insert num number of c's at offset atchar in a linebuf of LBSIZE */ 482: 483: ins_c(c, buf, atchar, num, max) 484: char c, *buf; 485: { 486: register char *pp, *pp1; 487: register int len; 488: int numchars; /* Number of characters to copy forward */ 489: 490: if (num <= 0) 491: return; 492: len = atchar + strlen(&buf[atchar]); 493: if (len + num >= max) 494: len_error(COMPLAIN); 495: pp = &buf[len + 1]; /* + 1 so we can --pp (not pp--) */ 496: pp1 = &buf[len + num + 1]; 497: numchars = len - atchar; 498: while (numchars-- >= 0) 499: *--pp1 = *--pp; 500: pp = &buf[atchar]; 501: while (--num >= 0) 502: *pp++ = c; 503: } 504: 505: TwoBlank() 506: { 507: register Line *next = curline->l_next; 508: 509: return ((next != 0) && 510: (*(lcontents(next)) == '\0') && 511: (next->l_next != 0) && 512: (*(lcontents(next->l_next)) == '\0')); 513: } 514: 515: linecopy(onto, atchar, from) 516: register char *onto, 517: *from; 518: { 519: register char *endp = &onto[LBSIZE - 2]; 520: 521: onto += atchar; 522: 523: while (*onto = *from++) 524: if (onto++ >= endp) 525: len_error(ERROR); 526: } 527: 528: char * 529: IOerr(err, file) 530: char *err, *file; 531: { 532: return sprint("Couldn't %s \"%s\".", err, file); 533: } 534: 535: pclose(p) 536: int *p; 537: { 538: (void) close(p[0]); 539: (void) close(p[1]); 540: } 541: 542: dopipe(p) 543: int p[]; 544: { 545: if (pipe(p) == -1) 546: complain("[Pipe failed]"); 547: } 548: 549: /* NOSTRICT */ 550: 551: char * 552: emalloc(size) 553: { 554: char *ptr; 555: 556: if (ptr = malloc((unsigned) size)) 557: return ptr; 558: /* Try garbage collecting lines */ 559: GCchunks(); 560: if (ptr = malloc((unsigned) size)) 561: return ptr; 562: /* Uh ... Oh screw it! */ 563: error("[Out of memory] "); 564: /* NOTREACHED */ 565: } 566: 567: /* Return the basename of file F. */ 568: 569: char * 570: basename(f) 571: register char *f; 572: { 573: register char *cp; 574: 575: if (cp = rindex(f, '/')) 576: return cp + 1; 577: else 578: return f; 579: } 580: 581: push_env(savejmp) 582: jmp_buf savejmp; 583: { 584: byte_copy((char *) mainjmp, (char *) savejmp, sizeof (jmp_buf)); 585: } 586: 587: pop_env(savejmp) 588: jmp_buf savejmp; 589: { 590: byte_copy((char *) savejmp, (char *) mainjmp, sizeof (jmp_buf)); 591: } 592: 593: #ifdef LOAD_AV 594: # ifdef BSD4_2 595: # ifdef PURDUE_EE 596: 597: get_la(dp) 598: double *dp; 599: { 600: *dp = (double) loadav(0) / 100.0; 601: } 602: 603: # else PURDUE_EE 604: 605: #include <nlist.h> 606: 607: static struct nlist nl[] = { 608: { "_avenrun" }, 609: #define X_AVENRUN 0 610: { "" } 611: }; 612: 613: get_la(dp) 614: double *dp; 615: { 616: double avenrun[3]; 617: static int kmem = 0; 618: 619: if (kmem == -1) { 620: *dp = 4.0; /* So shell commands will say "Chugging" */ 621: return; 622: } else if (kmem == 0) { 623: if ((kmem = open("/dev/kmem", 0)) == -1) { 624: f_mess("Can't open kmem for load average."); 625: *dp = 4.0; 626: return; 627: } 628: nlist("/vmunix", nl); 629: } 630: lseek(kmem, (long) nl[X_AVENRUN].n_value, 0); 631: read(kmem, (char *) avenrun, sizeof(avenrun)); 632: *dp = avenrun[0]; 633: } 634: 635: # endif PURDUE_EE 636: # else BSD4_2 637: 638: get_la(dp) 639: double *dp; 640: { 641: short avg[3]; 642: 643: gldav(avg); 644: *dp = (double) avg[0] / 256; 645: } 646: 647: # endif BSD4_2 648: #endif LOAD_AV 649: 650: /* get the time buf, designated by *timep, from FROM to TO. */ 651: char * 652: get_time(timep, buf, from, to) 653: char *buf; 654: time_t *timep; 655: { 656: time_t now; 657: char *cp; 658: extern char *ctime(); 659: 660: if (timep != 0) 661: now = *timep; 662: else 663: (void) time(&now); 664: cp = ctime(&now) + from; 665: if (to == -1) 666: cp[strlen(cp) - 1] = '\0'; /* Get rid of \n */ 667: else 668: cp[to - from] = '\0'; 669: if (buf) { 670: strcpy(buf, cp); 671: return buf; 672: } else 673: return cp; 674: } 675: 676: /* Return length of null terminated string. */ 677: 678: strlen(s) 679: register char *s; 680: { 681: register char *base = s + 1; /* Can you say kludge? */ 682: 683: while (*s++) 684: ; 685: return (s - base); 686: } 687: 688: char * 689: index(s, c) 690: register char *s; 691: register int c; 692: { 693: register int c1; 694: 695: if (c != 0) 696: while (c1 = *s++) 697: if (c == c1) 698: return s - 1; 699: return 0; 700: } 701: 702: strcmp(s1, s2) 703: register char *s1, 704: *s2; 705: { 706: if (!s1 || !s2) 707: return 1; /* Which is not zero ... */ 708: while (*s1 == *s2++) 709: if (*s1++ == '\0') 710: return 0; 711: return (*s1 - *--s2); 712: } 713: 714: casecmp(s1, s2) 715: register char *s1, 716: *s2; 717: { 718: if (!s1 || !s2) 719: return 1; /* Which is not zero ... */ 720: while (*s1 == *s2++ || Upper(*s1) == Upper(s2[-1])) 721: if (*s1++ == '\0') 722: return 0; 723: return (*s1 - *--s2); 724: } 725: 726: casencmp(s1, s2, n) 727: register char *s1, 728: *s2; 729: register int n; 730: { 731: if (!s1 || !s2) 732: return 1; /* Which is not zero ... */ 733: while (--n >= 0 && (*s1 == *s2++ || Upper(*s1) == Upper(s2[-1]))) 734: if (*s1++ == '\0') 735: return 0; 736: return ((n < 0) ? 0 : *s1 - *--s2); 737: } 738: 739: null_ncpy(to, from, n) 740: char *to, 741: *from; 742: { 743: (void) strncpy(to, from, n); 744: to[n] = '\0'; 745: } 746: 747: strcpy(t, f) 748: register char *t, 749: *f; 750: { 751: while (*t++ = *f++) 752: ; 753: } 754: 755: /* Tries to pause for delay/10 seconds OR until a character is typed 756: at the keyboard. This works well on BSD4_2 and not so well on the 757: rest. Returns 1 if it returned because of keyboard input, or 0 758: otherwise. */ 759: 760: SitFor(delay) 761: int delay; 762: { 763: #ifdef BSD4_2 764: #include <sys/time.h> 765: 766: struct timeval timer; 767: int readfds = 1, 768: writefds = 0, 769: exceptfds = 0; 770: 771: timer.tv_sec = (delay / 10); 772: timer.tv_usec = (delay % 10) * 100000; 773: 774: if (charp()) 775: return 1; 776: /* gross that I had to snarf this from getch() */ 777: if (!UpdMesg && !Asking) { /* Don't erase if we are asking */ 778: if (mesgbuf[0] && !errormsg) 779: message(NullStr); 780: } 781: redisplay(); 782: return select(1, &readfds, &writefds, &exceptfds, &timer); 783: #else 784: static float cps[] = { 785: 0.0, 786: 5.0, 787: 7.5, 788: 11.0, 789: 13.4, 790: 15.0, 791: 20.0, 792: 30.0, 793: 60.0, 794: 120.0, 795: 180.0, 796: 240.0, 797: 480.0, 798: 960.0, 799: 1920.0, 800: 1920.0, 801: }; 802: float nsecs; 803: register int nchars; 804: 805: if (charp()) 806: return 1; 807: nsecs = (float) delay / 10; 808: nchars = (int) (nsecs * cps[ospeed]); 809: redisplay(); 810: while ((--nchars > 0) && !InputPending) { 811: putchar(0); 812: if (OkayAbort) { 813: OkayAbort = 0; 814: InputPending = charp(); 815: } 816: } 817: return InputPending; 818: #endif 819: } 820: 821: sindex(pattern, string) 822: register char *pattern, 823: *string; 824: { 825: register int len = strlen(pattern); 826: 827: while (*string != '\0') { 828: if (*pattern == *string && strncmp(pattern, string, len) == 0) 829: return TRUE; 830: string++; 831: } 832: return FALSE; 833: }