/* * *rc file parser * Copyright * (C) 1992 Joseph H. Allen; * * This file is part of JOE (Joe's Own Editor) */ #include "config.h" #include "types.h" #include #include #include #ifdef HAVE_STDLIB_H #include #endif #include "cmd.h" #include "kbd.h" #include "macro.h" #include "menu.h" #include "path.h" #include "pw.h" #include "tw.h" #include "uedit.h" #include "umath.h" #include "utils.h" #include "vs.h" #include "w.h" #define OPT_BUF_SIZE 300 static struct context { struct context *next; char *name; KMAP *kmap; } *contexts = NULL; /* List of named contexts */ /* Find a context of a given name- if not found, one with an empty kmap * is created. */ KMAP *kmap_getcontext(unsigned char *name) { struct context *c; for (c = contexts; c; c = c->next) if (!strcmp(c->name, name)) return c->kmap; c = (struct context *) joe_malloc(sizeof(struct context)); c->next = contexts; c->name = strdup(name); contexts = c; return c->kmap = mkkmap(); } OPTIONS *options = NULL; extern int mid, dspasis, dspctrl, force, help, pgamnt, square, csmode, nobackups, lightoff, exask, skiptop, noxon, lines, staen, columns, Baud, dopadding, orphan, marking, beep, keepup, nonotice; extern char *backpath; OPTIONS pdefault = { NULL, /* *next */ NULL, /* *name */ 0, /* overtype */ 0, /* lmargin */ 76, /* rmargin */ 0, /* autoindent */ 0, /* wordwrap */ 8, /* tab */ ' ', /* indent char */ 1, /* indent step */ NULL, /* *context */ NULL, /* *lmsg */ NULL, /* *rmsg */ 0, /* line numbers */ 0, /* read only */ 0, /* french spacing */ 0, /* spaces */ #ifdef __MSDOS__ 1, /* crlf */ #else 0, /* crlf */ #endif NULL, /* macro to execute for new files */ NULL, /* macro to execute for existing files */ NULL, /* macro to execute before saving new files */ NULL, /* macro to execute before saving existing files */ }; OPTIONS fdefault = { NULL, /* *next */ NULL, /* *name */ 0, /* overtype */ 0, /* lmargin */ 76, /* rmargin */ 0, /* autoindent */ 0, /* wordwrap */ 8, /* tab */ ' ', /* indent char */ 1, /* indent step */ "main", /* *context */ "\\i%n %m %M", /* *lmsg */ " %S Ctrl-K H 線上輔助", /* *rmsg */ 0, /* line numbers */ 0, /* read only */ 0, /* french spacing */ 0, /* spaces */ #ifdef __MSDOS__ 1, /* crlf */ #else 0, /* crlf */ #endif NULL, NULL, NULL, NULL /* macros (see above) */ }; /* Set a global or local option * returns 0 for no such option, * 1 for option accepted * 2 for option + argument accepted */ void setopt(OPTIONS *n, char *name) { OPTIONS *o; for (o = options; o; o = o->next) if (rmatch(o->name, name)) { *n = *o; return; } *n = fdefault; } struct glopts { char *name; /* Option name */ int type; /* 0 for global option flag 1 for global option numeric 2 for global option string 4 for local option flag 5 for local option numeric 6 for local option string 7 for local option numeric+1 */ int *set; /* Address of global option */ char *addr; /* Local options structure member address */ char *yes; /* Message if option was turned on, or prompt string */ char *no; /* Message if option was turned off */ char *menu; /* Menu string */ int ofst; /* Local options structure member offset */ int low; /* Low limit for numeric options */ int high; /* High limit for numeric options */ } glopts[] = { { "overwrite", 4, NULL, (char *) &fdefault.overtype, "覆寫模式", "插入模式", "T [**輸入模式的設定(overwrite)**] " }, { "autoindent", 4, NULL, (char *) &fdefault.autoindent, "自動縮排 [開啟]", "自動縮排 [關閉]", "I [**自動縮排的設定(autoindent)**] " }, { "wordwrap", 4, NULL, (char *) &fdefault.wordwrap, "自動換行 [開啟]", "自動換行 [關閉]", "* [**自動換行的設定(wordwrap)**] " }, { "tab", 5, NULL, (char *) &fdefault.tab, "Tab 寬度 (%d): ", 0, "D [**Tab寬長的設定(tab)**] ", 0, 1, 64 }, { "lmargin", 7, NULL, (char *) &fdefault.lmargin, "設定左邊界 (%d): ", 0, "L [**設定左邊界(lmargin)**] ", 0, 0, 63 }, { "rmargin", 7, NULL, (char *) &fdefault.rmargin, "設定右邊界 (%d): ", 0, "* [**設定右邊界(rmargin)**] ", 0, 7, 255 }, { "square", 0, &square, NULL, "矩形選取", "行列選取", "X [**選取區塊模式(square)**] " }, { "indentc", 5, NULL, (char *) &fdefault.indentc, "設定縮排字元 %d (SPACE=32, TAB=9, ^C 中斷): ", 0, "* [**縮排長度的設定(indentc)**] ", 0, 0, 255 }, { "istep", 5, NULL, (char *) &fdefault.istep, "設定縮排段落 %d (^C 中斷): ", 0, "* [**設定縮排的段落(istep)**] ", 0, 1, 64 }, { "french", 4, NULL, (char *) &fdefault.french, "段落重排後多 1 個空白", "段落重排後多 2 個空白", "* [**設定法國式段落排列(french)**] " }, { "spaces", 4, NULL, (char *) &fdefault.spaces, "設定按下 [TAB] 後插入 [空白] 字元 ", "設定按下 [TAB] 後插入 [TAB ] 字元 ", "* [**設定 TAB 鍵用途(spaces)**] " }, { "mid", 0, &mid, NULL, "螢幕捲動後游標自動向中間對齊功\能 [開啟", "螢幕捲動後游標自動向中間對齊功\能 [關閉]", "* [**游標向中對齊(mid)**] " }, { "crlf", 4, NULL, (char *) &fdefault.crlf, "行的結尾是 CR-LF(DOS)", "行的結尾是 LF(UNIX)", "Z [*行尾字元的設定(crlf)*] " }, { "linums", 4, NULL, (char *) &fdefault.linums, "顯示行數功\能 [開啟]", "顯示行數功\能 [關閉]", "N [***行數顯示與否(linums)***] " }, { "marking", 0, &marking, NULL, "反白區塊隨游標移動功\能 [開啟]", "反白區塊隨游標移動功\能 [關閉]", "* [**區塊反白設定(marking)**] " }, { "asis", 0, &dspasis, NULL, "顯示 ASCII 127 以上的字元", "以相反方式來顯示 ASCII 127 以上的字元", "* [**中文顯示方式(asis)**] " }, { "force", 0, &force, NULL, "存檔後,檔尾自動加上一行空白行功\能 [開啟]", "存檔後,檔尾自動加上一行空白行功\能 [關閉]", "* [**存檔時檔尾的設定(force)**] " }, { "nobackups", 0, &nobackups, NULL, "自動產生備份檔功\能 [關閉]", "自動產生備份檔功\能 [開啟]", "* [**自動備份的設定(nobackups)**] " }, { "lightoff", 0, &lightoff, NULL, "使用區塊功\能後,自動關閉高亮度區塊功\能 [關閉]", "使用區塊功\能後,自動關閉高亮度區塊功\能 [開啟]", "* [**區塊亮度設定**] " }, { "exask", 0, &exask, NULL, "離開時是否問要存的檔名 [是的]", "離開時是否問要存的檔名 [不要]", "* [**離開時詢問要存檔的檔名(exask)** " }, { "beep", 0, &beep, NULL, "錯誤時發出\"嗶\"的聲音功\能 [開啟]", "錯誤時發出\"嗶\"的聲音功\能 [關閉]", "* [**錯誤時,是否發\"嗶\"聲(beep)**] " }, { "nosta", 0, &staen, NULL, "開啟最上面的狀態列功\能[關閉]", "開啟最上面的狀態列功\能[開啟]", "* [**狀態列開關(nosta)**] " }, { "keepup", 0, &keepup, NULL, "狀態列固定時間更新資料一次", "狀態列每秒更新資料一次", "* [**狀態列資料更新(keepup)**] " }, { "pg", 1, &pgamnt, NULL, "按下 [PgUp]/[PgDn] 時所保留的行數 或 -1(保留半個視窗) (%d): ", 0, "* [**按PaUp/PaDn 時的行數(pg)**] ", 0, -1, 64 }, { "csmode", 0, &csmode, NULL, "按下 CTRL + F 以前面所輸入的字串開始搜尋", "按下 CTRL + F 都以新的字串進行搜尋", "* [**搜尋字元的設定(csmode)**] " }, { "rdonly", 4, NULL, (char *) &fdefault.readonly, "唯讀模式", "可編輯模式", "O [**唯讀模式設定(rdonly)**] " }, { "backpath", 2, (int *) &backpath, NULL, "備份檔儲存路徑(%s): ", 0, "* [**備份檔案路徑(backpath)**] " }, { "nonotice", 0, &nonotice, NULL, 0, 0, 0 }, { "noxon", 0, &noxon, NULL, 0, 0, 0 }, { "orphan", 0, &orphan, NULL, 0, 0, 0 }, { "help", 0, &help, NULL, 0, 0, 0 }, { "dopadding", 0, &dopadding, NULL, 0, 0, 0 }, { "lines", 1, &lines, NULL, 0, 0, 0, 0, 2, 1024 }, { "baud", 1, &Baud, NULL, 0, 0, 0, 0, 50, 32767 }, { "columns", 1, &columns, NULL, 0, 0, 0, 0, 2, 1024 }, { "skiptop", 1, &skiptop, NULL, 0, 0, 0, 0, 0, 64 }, { NULL, 0, NULL, NULL, NULL, NULL, NULL, 0, 0, 0 } }; int isiz = 0; static void izopts(void) { int x; for (x = 0; glopts[x].name; ++x) switch (glopts[x].type) { case 4: case 5: case 6: case 7: case 8: glopts[x].ofst = glopts[x].addr - (char *) &fdefault; } isiz = 1; } int glopt(unsigned char *s, unsigned char *arg, OPTIONS *options, int set) { int val; int ret = 0; int st = 1; int x; if (!isiz) izopts(); if (s[0] == '-') { st = 0; ++s; } for (x = 0; glopts[x].name; ++x) if (!strcmp(glopts[x].name, s)) { switch (glopts[x].type) { case 0: if (set) *glopts[x].set = st; break; case 1: if (set && arg) { sscanf(arg, "%d", &val); if (val >= glopts[x].low && val <= glopts[x].high) *glopts[x].set = val; } break; case 2: if (set) { if (arg) *(char **) glopts[x].set = strdup(arg); else *(char **) glopts[x].set = 0; } break; case 4: if (options) *(int *) ((char *) options + glopts[x].ofst) = st; else if (set == 2) *(int *) ((char *) &fdefault + glopts[x].ofst) = st; break; case 5: if (arg) { if (options) { sscanf(arg, "%d", &val); if (val >= glopts[x].low && val <= glopts[x].high) *(int *) ((char *) options + glopts[x].ofst) = val; } else if (set == 2) { sscanf(arg, "%d", &val); if (val >= glopts[x].low && val <= glopts[x].high) *(int *) ((char *) &fdefault + glopts[x].ofst) = val; } } break; case 7: if (arg) { int zz = 0; sscanf(arg, "%d", &zz); if (zz >= glopts[x].low && zz <= glopts[x].high) { --zz; if (options) *(int *) ((char *) options + glopts[x].ofst) = zz; else if (set == 2) *(int *) ((char *) &fdefault + glopts[x].ofst) = zz; } } break; } if ((glopts[x].type & 3) == 0 || !arg) return 1; else return 2; } if (!strcmp(s, "lmsg")) { if (arg) { if (options) options->lmsg = strdup(arg); else if (set == 2) fdefault.lmsg = strdup(arg); ret = 2; } else ret = 1; } else if (!strcmp(s, "rmsg")) { if (arg) { if (options) options->rmsg = strdup(arg); else if (set == 2) fdefault.rmsg = strdup(arg); ret = 2; } else ret = 1; } else if (!strcmp(s, "keymap")) { if (arg) { int y; for (y = 0; !isspace(arg[y]); ++y) ; if (!arg[y]) arg[y] = 0; if (options && y) options->context = strdup(arg); ret = 2; } else ret = 1; } else if (!strcmp(s, "mnew")) { if (arg) { int sta; if (options) options->mnew = mparse(NULL, arg, &sta); else if (set == 2) fdefault.mnew = mparse(NULL, arg, &sta); ret = 2; } else ret = 1; } else if (!strcmp(s, "mold")) { if (arg) { int sta; if (options) options->mold = mparse(NULL, arg, &sta); else if (set == 2) fdefault.mold = mparse(NULL, arg, &sta); ret = 2; } else ret = 1; } else if (!strcmp(s, "msnew")) { if (arg) { int sta; if (options) options->msnew = mparse(NULL, arg, &sta); else if (set == 2) fdefault.msnew = mparse(NULL, arg, &sta); ret = 2; } else ret = 1; } else if (!strcmp(s, "msold")) { if (arg) { int sta; if (options) options->msold = mparse(NULL, arg, &sta); else if (set == 2) fdefault.msold = mparse(NULL, arg, &sta); ret = 2; } else ret = 1; } return ret; } static int optx = 0; static int doabrt1(BW *bw, int *xx) { joe_free(xx); return -1; } static int doopt1(BW *bw, char *s, int *xx, int *notify) { int ret = 0; int x = *xx; int v; joe_free(xx); switch (glopts[x].type) { case 1: v = calc(bw, s); if (merr) { msgnw(bw->parent, merr); ret = -1; } else if (v >= glopts[x].low && v <= glopts[x].high) *glopts[x].set = v; else { msgnw(bw->parent, "數值超過範圍"); ret = -1; } break; case 2: if (s[0]) *(char **) glopts[x].set = strdup(s); break; case 5: v = calc(bw, s); if (merr) { msgnw(bw->parent, merr); ret = -1; } else if (v >= glopts[x].low && v <= glopts[x].high) *(int *) ((char *) &bw->o + glopts[x].ofst) = v; else { msgnw(bw->parent, "數值超過範圍"); ret = -1; } break; case 7: v = calc(bw, s) - 1.0; if (merr) { msgnw(bw->parent, merr); ret = -1; } else if (v >= glopts[x].low && v <= glopts[x].high) *(int *) ((char *) &bw->o + glopts[x].ofst) = v; else { msgnw(bw->parent, "數值超過範圍"); ret = -1; } break; } vsrm(s); bw->b->o = bw->o; wfit(bw->parent->t); updall(); if (notify) *notify = 1; return ret; } static int doopt(MENU *m, int x, void *object, int flg) { BW *bw = m->parent->win->object; int *xx; char buf[OPT_BUF_SIZE]; int *notify = m->parent->notify; switch (glopts[x].type) { case 0: if (!flg) *glopts[x].set = !*glopts[x].set; else if (flg == 1) *glopts[x].set = 1; else *glopts[x].set = 0; wabort(m->parent); msgnw(bw->parent, *glopts[x].set ? glopts[x].yes : glopts[x].no); break; case 4: if (!flg) *(int *) ((char *) &bw->o + glopts[x].ofst) = !*(int *) ((char *) &bw->o + glopts[x].ofst); else if (flg == 1) *(int *) ((char *) &bw->o + glopts[x].ofst) = 1; else *(int *) ((char *) &bw->o + glopts[x].ofst) = 0; wabort(m->parent); msgnw(bw->parent, *(int *) ((char *) &bw->o + glopts[x].ofst) ? glopts[x].yes : glopts[x].no); if (glopts[x].ofst == (char *) &fdefault.readonly - (char *) &fdefault) bw->b->rdonly = bw->o.readonly; break; case 1: snprintf(buf, OPT_BUF_SIZE, glopts[x].yes, *glopts[x].set); xx = (int *) joe_malloc(sizeof(int)); *xx = x; m->parent->notify = 0; wabort(m->parent); if (wmkpw(bw->parent, buf, NULL, doopt1, NULL, doabrt1, utypebw, xx, notify)) return 0; else return -1; case 2: if (*(char **) glopts[x].set) snprintf(buf, OPT_BUF_SIZE, glopts[x].yes, *(char **) glopts[x].set); else snprintf(buf, OPT_BUF_SIZE, glopts[x].yes, ""); xx = (int *) joe_malloc(sizeof(int)); *xx = x; m->parent->notify = 0; wabort(m->parent); if (wmkpw(bw->parent, buf, NULL, doopt1, NULL, doabrt1, utypebw, xx, notify)) return 0; else return -1; case 5: snprintf(buf, OPT_BUF_SIZE, glopts[x].yes, *(int *) ((char *) &bw->o + glopts[x].ofst)); goto in; case 7: snprintf(buf, OPT_BUF_SIZE, glopts[x].yes, *(int *) ((char *) &bw->o + glopts[x].ofst) + 1); in:xx = (int *) joe_malloc(sizeof(int)); *xx = x; m->parent->notify = 0; wabort(m->parent); if (wmkpw(bw->parent, buf, NULL, doopt1, NULL, doabrt1, utypebw, xx, notify)) return 0; else return -1; } if (notify) *notify = 1; bw->b->o = bw->o; wfit(bw->parent->t); updall(); return 0; } static int doabrt(MENU *m, int x, char **s) { optx = x; for (x = 0; s[x]; ++x) joe_free(s[x]); joe_free(s); return -1; } int umode(BW *bw) { int size; char **s; int x; bw->b->o.readonly = bw->o.readonly = bw->b->rdonly; for (size = 0; glopts[size].menu; ++size) ; s = (char **) joe_malloc(sizeof(char *) * (size + 1)); for (x = 0; x != size; ++x) { s[x] = (char *) joe_malloc(40); /* FIXME: why 40 ??? */ switch (glopts[x].type) { case 0: snprintf(s[x], OPT_BUF_SIZE, "%s%s", glopts[x].menu, *glopts[x].set ? "開" : "關"); break; case 1: snprintf(s[x], OPT_BUF_SIZE, "%s%d", glopts[x].menu, *glopts[x].set); break; case 2: strcpy(s[x], glopts[x].menu); break; case 4: snprintf(s[x], OPT_BUF_SIZE, "%s%s", glopts[x].menu, *(int *) ((char *) &bw->o + glopts[x].ofst) ? "開" : "關"); break; case 5: snprintf(s[x], OPT_BUF_SIZE, "%s%d", glopts[x].menu, *(int *) ((char *) &bw->o + glopts[x].ofst)); break; case 7: snprintf(s[x], OPT_BUF_SIZE, "%s%d", glopts[x].menu, *(int *) ((char *) &bw->o + glopts[x].ofst) + 1); break; } } s[x] = 0; if (mkmenu(bw->parent, s, doopt, doabrt, NULL, optx, s, NULL)) return 0; else return -1; } /* Process rc file * Returns 0 if the rc file was succefully processed * -1 if the rc file couldn't be opened * 1 if there was a syntax error in the file */ int procrc(CAP *cap, unsigned char *name) { OPTIONS *o = NULL; /* Current options */ KMAP *context = NULL; /* Current context */ unsigned char buf[1024]; /* Input buffer */ FILE *fd; /* rc file */ int line = 0; /* Line number */ int err = 0; /* Set to 1 if there was a syntax error */ strcpy(buf, name); #ifdef __MSDOS__ fd = fopen(buf, "rt"); #else fd = fopen(buf, "r"); #endif if (!fd) return -1; /* Return if we couldn't open the rc file */ fprintf(stderr, "'%s'處理中...", name); fflush(stderr); while (fgets(buf, sizeof(buf), fd)) { line++; switch (buf[0]) { case ' ': case '\t': case '\n': case '\f': case 0: break; /* Skip comment lines */ case '*': /* Select file types for file-type dependant options */ { int x; o = (OPTIONS *) joe_malloc(sizeof(OPTIONS)); *o = fdefault; for (x = 0; buf[x] && buf[x] != '\n' && buf[x] != ' ' && buf[x] != '\t'; ++x) ; buf[x] = 0; o->next = options; options = o; o->name = strdup(buf); } break; case '-': /* Set an option */ { unsigned char *opt = buf + 1; int x; unsigned char *arg = NULL; for (x = 0; buf[x] && buf[x] != '\n' && buf[x] != ' ' && buf[x] != '\t'; ++x) ; if (buf[x] && buf[x] != '\n') { buf[x] = 0; for (arg = buf + ++x; buf[x] && buf[x] != '\n'; ++x) ; } buf[x] = 0; if (!glopt(opt, arg, o, 2)) { err = 1; fprintf(stderr, "\n%s %d: 未定義的設定 %s", name, line, opt); } } break; case '{': /* Ignore help text */ { while ((fgets(buf, 256, fd)) && (buf[0] != '}')) /* do nothing */; if (buf[0] != '}') { err = 1; fprintf(stderr, "\n%s %d: 因為 joerc 內容中的 help(說明文字)有不正常的結束,所以結束 joe 的讀取\n", name, line); break; } } break; case ':': /* Select context */ { int x, c; for (x = 1; !isspace_eof(buf[x]); ++x) ; c = buf[x]; buf[x] = 0; if (x != 1) if (!strcmp(buf + 1, "def")) { int y; for (buf[x] = c; isblank(buf[x]); ++x) ; for (y = x; !isspace_eof(buf[y]); ++y) ; c = buf[y]; buf[y] = 0; if (y != x) { int sta; MACRO *m; if (isblank(c) && (m = mparse(NULL, buf + y + 1, &sta))) addcmd(buf + x, m); else { err = 1; fprintf(stderr, "\n%s %d: 找不到巨集 :def", name, line); } } else { err = 1; fprintf(stderr, "\n%s %d: 找不到指令 :def", name, line); } } else if (!strcmp(buf + 1, "inherit")) if (context) { for (buf[x] = c; isblank(buf[x]); ++x) ; for (c = x; !isspace_eof(buf[c]); ++c) ; buf[c] = 0; if (c != x) kcpy(context, kmap_getcontext(buf + x)); else { err = 1; fprintf(stderr, "\n%s %d: 找不到內容 :inherit", name, line); } } else { err = 1; fprintf(stderr, "\n%s %d: 沒有選定任何的內容 :inherit", name, line); } else if (!strcmp(buf + 1, "include")) { for (buf[x] = c; isblank(buf[x]); ++x) ; for (c = x; !isspace_eof(buf[c]); ++c) ; buf[c] = 0; if (c != x) { switch (procrc(cap, buf + x)) { case 1: err = 1; break; case -1: fprintf(stderr, "\n%s %d: 無法開啟 %s", name, line, buf + x); err = 1; break; } context = 0; o = 0; } else { err = 1; fprintf(stderr, "\n%s %d: : 無法找到相關 include 檔", name, line); } } else if (!strcmp(buf + 1, "delete")) if (context) { int y; for (buf[x] = c; isblank(buf[x]); ++x) ; for (y = x; buf[y] != 0 && buf[y] != '\t' && buf[y] != '\n' && (buf[y] != ' ' || buf[y + 1] != ' '); ++y) ; buf[y] = 0; kdel(context, buf + x); } else { err = 1; fprintf(stderr, "\n%s %d: 沒有選定任何的內容 :delete", name, line); } else context = kmap_getcontext(buf + 1); else { err = 1; fprintf(stderr, "\n%s %d: 不正確的名稱", name, line); } } break; default: /* Get key-sequence to macro binding */ { int x, y; MACRO *m; if (!context) { err = 1; fprintf(stderr, "\n%s %d: 未選定任何內容給巨集使用", name, line); break; } m = 0; macroloop: m = mparse(m, buf, &x); if (x == -1) { err = 1; fprintf(stderr, "\n%s %d: 巨集內有未知的的指令", name, line); break; } else if (x == -2) { fgets(buf, 1024, fd); goto macroloop; } if (!m) break; /* Skip to end of key sequence */ for (y = x; buf[y] != 0 && buf[y] != '\t' && buf[y] != '\n' && (buf[y] != ' ' || buf[y + 1] != ' '); ++y) ; buf[y] = 0; /* Add binding to context */ if (kadd(cap, context, buf + x, m) == -1) { fprintf(stderr, "\n%s %d: 錯誤的按鍵順序設定 '%s'", name, line, buf + x); err = 1; } } break; } } fclose(fd); /* Close rc file */ /* Print proper ending string */ if (err) fprintf(stderr, "\n完成\n"); else fprintf(stderr, "完成\n"); return err; /* 0 for success, 1 for syntax error */ }