| 1 | #include "tweak.h" |
| 2 | |
| 3 | #include <stdio.h> |
| 4 | #include <stdlib.h> |
| 5 | #include <string.h> |
| 6 | #include <ctype.h> |
| 7 | |
| 8 | #if defined(unix) && !defined(GO32) |
| 9 | #define RCNAME ".tweakrc" |
| 10 | #elif defined(MSDOS) |
| 11 | #define RCNAME "tweak.rc" |
| 12 | #endif |
| 13 | |
| 14 | static char *default_rc[] = { |
| 15 | "# Default "RCNAME" generated by `tweak -D'.", |
| 16 | "#", |
| 17 | "# Key bindings: movement keys", |
| 18 | "bind top-of-file ^[<", |
| 19 | #if defined(unix) && !defined(GO32) |
| 20 | "bind page-up ^[[5~", |
| 21 | #elif defined(MSDOS) |
| 22 | "bind page-up ^@I", |
| 23 | "bind page-up ^@/", |
| 24 | #endif |
| 25 | "bind page-up ^[V", |
| 26 | "bind page-up ^[v", |
| 27 | "bind move-up ^P", |
| 28 | #if defined(unix) && !defined(GO32) |
| 29 | "bind move-up ^[[A", |
| 30 | #elif defined(MSDOS) |
| 31 | "bind move-up ^@H", |
| 32 | #endif |
| 33 | "bind begin-line ^A", |
| 34 | #if defined(unix) && !defined(GO32) |
| 35 | "bind begin-line ^[[H", |
| 36 | "bind begin-line ^[[1~", |
| 37 | #elif defined(MSDOS) |
| 38 | "bind begin-line ^@G", |
| 39 | #endif |
| 40 | "bind move-left ^B", |
| 41 | #if defined(unix) && !defined(GO32) |
| 42 | "bind move-left ^[[D", |
| 43 | #elif defined(MSDOS) |
| 44 | "bind move-left ^@K", |
| 45 | #endif |
| 46 | "bind move-right ^F", |
| 47 | #if defined(unix) && !defined(GO32) |
| 48 | "bind move-right ^[[C", |
| 49 | #elif defined(MSDOS) |
| 50 | "bind move-right ^@M", |
| 51 | #endif |
| 52 | "bind end-line ^E", |
| 53 | #if defined(unix) && !defined(GO32) |
| 54 | "bind end-line ^[Ow", |
| 55 | "bind end-line ^[[4~", |
| 56 | #elif defined(MSDOS) |
| 57 | "bind end-line ^@O", |
| 58 | #endif |
| 59 | "bind move-down ^N", |
| 60 | #if defined(unix) && !defined(GO32) |
| 61 | "bind move-down ^[[B", |
| 62 | #elif defined(MSDOS) |
| 63 | "bind move-down ^@P", |
| 64 | #endif |
| 65 | "bind page-down ^V", |
| 66 | #if defined(unix) && !defined(GO32) |
| 67 | "bind page-down ^[[6~", |
| 68 | #elif defined(MSDOS) |
| 69 | "bind page-down ^@Q", |
| 70 | #endif |
| 71 | "bind bottom-of-file ^[>", |
| 72 | "", |
| 73 | "# Key bindings: miscellaneous editing keys", |
| 74 | "bind toggle-insert ^X^I", |
| 75 | #if defined(unix) && !defined(GO32) |
| 76 | "bind toggle-insert ^[[2~", |
| 77 | #elif defined(MSDOS) |
| 78 | "bind toggle-insert ^@R", |
| 79 | #endif |
| 80 | "bind change-mode ^M", |
| 81 | "bind change-mode ^J", |
| 82 | "bind quote-next ^Q", |
| 83 | "bind toggle-status ^XH", |
| 84 | "bind toggle-status ^Xh", |
| 85 | "bind toggle-status ^XX", |
| 86 | "bind toggle-status ^Xx", |
| 87 | "", |
| 88 | "# Key bindings: deletion keys", |
| 89 | "bind delete-left ^?", |
| 90 | "bind delete-left ^H", |
| 91 | "bind delete-right ^D", |
| 92 | #if defined(unix) && !defined(GO32) |
| 93 | "bind delete-right ^[[3~", |
| 94 | #elif defined(MSDOS) |
| 95 | "bind delete-right ^@S", |
| 96 | #endif |
| 97 | "", |
| 98 | "# Key bindings: cut and paste keys", |
| 99 | #if defined(unix) && !defined(GO32) |
| 100 | "bind mark-place ^@", |
| 101 | #elif defined(MSDOS) |
| 102 | "bind mark-place ^@^C", |
| 103 | #endif |
| 104 | "bind cut ^W", |
| 105 | "bind copy ^[W", |
| 106 | "bind copy ^[w", |
| 107 | #ifdef MSDOS |
| 108 | "bind copy ^@^Q", |
| 109 | #endif |
| 110 | "bind paste ^Y", |
| 111 | "", |
| 112 | "# Key bindings: additional movement keys", |
| 113 | "bind search ^S", |
| 114 | "bind search-back ^R", |
| 115 | "bind goto-position ^XG", |
| 116 | "bind goto-position ^Xg", |
| 117 | "bind screen-recentre ^L", |
| 118 | "", |
| 119 | "# Standard screen size parameters, plus keybindings to alter them", |
| 120 | "width 16", |
| 121 | "offset 0", |
| 122 | "bind new-width ^XW", |
| 123 | "bind new-width ^Xw", |
| 124 | "bind new-offset ^XO", |
| 125 | "bind new-offset ^Xo", |
| 126 | "", |
| 127 | "# Key bindings: overall program/file control", |
| 128 | "bind suspend ^Z", |
| 129 | "bind exit ^X^C", |
| 130 | "bind save-file ^X^S", |
| 131 | "# unbound by default: exit-and-save", |
| 132 | "", |
| 133 | #ifdef TEST_BUFFER |
| 134 | "bind diagnostics ^X^D", |
| 135 | "", |
| 136 | #endif |
| 137 | "# End of default "RCNAME, |
| 138 | NULL |
| 139 | }; |
| 140 | |
| 141 | extern char *pname; |
| 142 | |
| 143 | void read_rc (void) { |
| 144 | FILE *fp; |
| 145 | char **p, *q, *r, *s, *keyseq; |
| 146 | char rcbuffer[256]; |
| 147 | char rcname[FILENAME_MAX]; |
| 148 | int lineno = 0; |
| 149 | int errors = FALSE, errors_here; |
| 150 | |
| 151 | #if defined(unix) && !defined(GO32) |
| 152 | rcname[0] = '\0'; |
| 153 | if (getenv("HOME")) |
| 154 | strcpy (rcname, getenv("HOME")); |
| 155 | strcat (rcname, "/.tweakrc"); |
| 156 | #elif defined(MSDOS) |
| 157 | /* |
| 158 | * Use environment variable TWEAKRC if set. Otherwise, look for |
| 159 | * TWEAK.RC in the same directory as TWEAK.EXE, if _that_ exists, |
| 160 | * and failing everything else, try C:\TWEAK\TWEAK.RC. |
| 161 | */ |
| 162 | if (getenv("TWEAKRC")) |
| 163 | strcpy (rcname, getenv("TWEAKRC")); |
| 164 | else { |
| 165 | if ( (q = strrchr(pname, '\\')) != NULL) { |
| 166 | FILE *tempfp; |
| 167 | |
| 168 | strncpy (rcname, pname, q+1-pname); |
| 169 | strcpy (rcname+(q+1-pname), "TWEAK.RC"); |
| 170 | if ( (tempfp = fopen(rcname, "r")) != NULL) |
| 171 | fclose (tempfp); |
| 172 | else |
| 173 | strcpy (rcname, "C:\\TWEAK\\TWEAK.RC"); |
| 174 | } else |
| 175 | strcpy (rcname, "C:\\TWEAK\\TWEAK.RC"); |
| 176 | } |
| 177 | #endif |
| 178 | |
| 179 | { /* easy keybindings: self inserts */ |
| 180 | int i; |
| 181 | char c; |
| 182 | for (i=32; i<127; i++) { |
| 183 | c = i; |
| 184 | bind_key (&c, 1, act_self_ins); |
| 185 | } |
| 186 | } |
| 187 | |
| 188 | fp = fopen(rcname, "r"); |
| 189 | p = default_rc; |
| 190 | for (EVER) { |
| 191 | if (fp) { |
| 192 | if (!fgets(rcbuffer, sizeof(rcbuffer), fp)) { |
| 193 | fclose (fp); |
| 194 | break; |
| 195 | } |
| 196 | rcbuffer[strcspn(rcbuffer, "\r\n")] = '\0'; |
| 197 | } else { |
| 198 | if (!*p) |
| 199 | break; |
| 200 | strcpy (rcbuffer, *p++); |
| 201 | } |
| 202 | lineno++; |
| 203 | errors_here = FALSE; |
| 204 | |
| 205 | /* |
| 206 | * Now we have a line from the .rc file, wherever it's |
| 207 | * really come from. Process it. |
| 208 | */ |
| 209 | q = rcbuffer; |
| 210 | while (*q && isspace((unsigned char)*q)) |
| 211 | q++; |
| 212 | |
| 213 | if (!*q || *q == '#') |
| 214 | continue; /* comment or blank line */ |
| 215 | |
| 216 | r = q; |
| 217 | while (*r && !isspace((unsigned char)*r)) |
| 218 | r++; |
| 219 | if (*r) |
| 220 | *r++ = '\0'; |
| 221 | |
| 222 | /* |
| 223 | * Now "q" points to the command word, "r" to the rest of |
| 224 | * the line. |
| 225 | */ |
| 226 | if (!strcmp(q, "bind")) { |
| 227 | /* |
| 228 | * It's a "bind" directive. The rest of the line should |
| 229 | * consist of an action name, then a single whitespace |
| 230 | * character, then a key sequence. |
| 231 | */ |
| 232 | keyact action; |
| 233 | |
| 234 | while (*r && isspace((unsigned char)*r)) |
| 235 | r++; |
| 236 | |
| 237 | q = r; |
| 238 | while (*q && !isspace((unsigned char)*q)) |
| 239 | q++; |
| 240 | if (*q) |
| 241 | *q++ = '\0'; |
| 242 | else { |
| 243 | fprintf(stderr, "%s: no key sequence after \"bind\" command" |
| 244 | " on line %d of "RCNAME, pname, lineno); |
| 245 | errors = TRUE; |
| 246 | continue; |
| 247 | } |
| 248 | |
| 249 | /* |
| 250 | * "r" points to the action name; "q" to the key sequence. |
| 251 | */ |
| 252 | keyseq = s = q; |
| 253 | while (*q) { |
| 254 | if (*q == '^') { |
| 255 | if (!*++q) { |
| 256 | fprintf(stderr, "%s: nothing follows `^' on line %d" |
| 257 | " of "RCNAME, pname, lineno); |
| 258 | errors = TRUE; |
| 259 | errors_here = TRUE; |
| 260 | } else { |
| 261 | *s++ = *q++ ^ 0x40; |
| 262 | } |
| 263 | } else if (*q == '\\') { |
| 264 | if (!*++q) { |
| 265 | fprintf(stderr, "%s: nothing follows `\\' on line %d" |
| 266 | " of "RCNAME, pname, lineno); |
| 267 | errors = TRUE; |
| 268 | errors_here = TRUE; |
| 269 | } else if (*q == '\\' || *q == '^') { |
| 270 | *s++ = *q++; |
| 271 | } else if (isxdigit((unsigned char)*q) && |
| 272 | q[1] && isxdigit((unsigned char)q[1])) { |
| 273 | char buf[3]; |
| 274 | buf[0] = *q++; |
| 275 | buf[1] = *q++; |
| 276 | buf[2] = '\0'; |
| 277 | *s++ = strtol (buf, NULL, 16); |
| 278 | } else { |
| 279 | fprintf(stderr, "%s: badly formed `\\' sequence on" |
| 280 | " line %d of "RCNAME, pname, lineno); |
| 281 | errors = TRUE; |
| 282 | errors_here = TRUE; |
| 283 | } |
| 284 | } else |
| 285 | *s++ = *q++; |
| 286 | } |
| 287 | if (errors_here) |
| 288 | continue; |
| 289 | |
| 290 | if (!strcmp(r, "quote-next")) { |
| 291 | /* |
| 292 | * The "quote next" sequence requires special |
| 293 | * treatment. |
| 294 | */ |
| 295 | int i; |
| 296 | |
| 297 | for (i=0; i<256; i++) { |
| 298 | *s = i; |
| 299 | bind_key (keyseq, s-keyseq+1, act_self_ins); |
| 300 | } |
| 301 | } else if ( (action = parse_action (r)) ) { |
| 302 | /* |
| 303 | * An ordinary action, requiring ordinary treatment. |
| 304 | */ |
| 305 | bind_key (keyseq, s-keyseq, action); |
| 306 | } else { |
| 307 | fprintf(stderr, "%s: unrecognised key action \"%s\"" |
| 308 | " at line %d of "RCNAME"\n", |
| 309 | pname, r, lineno); |
| 310 | errors = TRUE; |
| 311 | } |
| 312 | } else if (!strcmp(q, "width")) { |
| 313 | width = atoi(r); |
| 314 | } else if (!strcmp(q, "offset")) { |
| 315 | realoffset = atoi(r); |
| 316 | } else { |
| 317 | fprintf(stderr, "%s: unrecognised "RCNAME" directive \"%s\"" |
| 318 | " at line %d of "RCNAME"\n", |
| 319 | pname, q, lineno); |
| 320 | errors = TRUE; |
| 321 | } |
| 322 | } |
| 323 | if (errors) |
| 324 | exit(1); |
| 325 | } |
| 326 | |
| 327 | void write_default_rc (void) { |
| 328 | char **p; |
| 329 | |
| 330 | for (p = default_rc; *p; p++) |
| 331 | puts (*p); |
| 332 | } |