/* Parse an argument, with optional quotes, after a keyword that takes * one. If the next word starts with a ", we say that it ends with the * last " of the line. Otherwise, we interpret it as usual, so that the * arguments can contain "'s too. */ char *parse_argument(char *ptr) { const char *ptr_save = ptr; char *last_quote = NULL; assert(ptr != NULL); if (*ptr != '"') return parse_next_word(ptr); do { ptr++; if (*ptr == '"') last_quote = ptr; } while (*ptr != '\0'); if (last_quote == NULL) { if (*ptr == '\0') ptr = NULL; else *ptr++ = '\0'; rcfile_error(N_("Argument '%s' has an unterminated \""), ptr_save); } else { *last_quote = '\0'; ptr = last_quote + 1; } if (ptr != NULL) while (isblank(*ptr)) ptr++; return ptr; }
/* Parse the rcfile, once it has been opened successfully at rcstream, * and close it afterwards. If syntax_only is TRUE, only allow the file * to contain color syntax commands: syntax, color, and icolor. */ void parse_rcfile(FILE *rcstream #ifdef ENABLE_COLOR , bool syntax_only #endif ) { char *buf = NULL; ssize_t len; size_t n; while ((len = getline(&buf, &n, rcstream)) > 0) { char *ptr, *keyword, *option; int set = 0; size_t i; /* Ignore the newline. */ if (buf[len - 1] == '\n') buf[len - 1] = '\0'; lineno++; ptr = buf; while (isblank(*ptr)) ptr++; /* If we have a blank line or a comment, skip to the next * line. */ if (*ptr == '\0' || *ptr == '#') continue; /* Otherwise, skip to the next space. */ keyword = ptr; ptr = parse_next_word(ptr); /* Try to parse the keyword. */ if (strcasecmp(keyword, "set") == 0) { #ifdef ENABLE_COLOR if (syntax_only) rcfile_error( N_("Command \"%s\" not allowed in included file"), keyword); else #endif set = 1; } else if (strcasecmp(keyword, "unset") == 0) { #ifdef ENABLE_COLOR if (syntax_only) rcfile_error( N_("Command \"%s\" not allowed in included file"), keyword); else #endif set = -1; } #ifdef ENABLE_COLOR else if (strcasecmp(keyword, "include") == 0) { if (syntax_only) rcfile_error( N_("Command \"%s\" not allowed in included file"), keyword); else parse_include(ptr); } else if (strcasecmp(keyword, "syntax") == 0) { if (endsyntax != NULL && endcolor == NULL) rcfile_error(N_("Syntax \"%s\" has no color commands"), endsyntax->desc); parse_syntax(ptr); } else if (strcasecmp(keyword, "header") == 0) parse_headers(ptr); else if (strcasecmp(keyword, "color") == 0) parse_colors(ptr, FALSE); else if (strcasecmp(keyword, "icolor") == 0) parse_colors(ptr, TRUE); else if (strcasecmp(keyword, "bind") == 0) parse_keybinding(ptr); #endif /* ENABLE_COLOR */ else rcfile_error(N_("Command \"%s\" not understood"), keyword); if (set == 0) continue; if (*ptr == '\0') { rcfile_error(N_("Missing flag")); continue; } option = ptr; ptr = parse_next_word(ptr); for (i = 0; rcopts[i].name != NULL; i++) { if (strcasecmp(option, rcopts[i].name) == 0) { #ifdef DEBUG fprintf(stderr, "parse_rcfile(): name = \"%s\"\n", rcopts[i].name); #endif if (set == 1) { if (rcopts[i].flag != 0) /* This option has a flag, so it doesn't take an * argument. */ SET(rcopts[i].flag); else { /* This option doesn't have a flag, so it takes * an argument. */ if (*ptr == '\0') { rcfile_error( N_("Option \"%s\" requires an argument"), rcopts[i].name); break; } option = ptr; if (*option == '"') option++; ptr = parse_argument(ptr); option = mallocstrcpy(NULL, option); #ifdef DEBUG fprintf(stderr, "option = \"%s\"\n", option); #endif /* Make sure option is a valid multibyte * string. */ if (!is_valid_mbstring(option)) { rcfile_error( N_("Option is not a valid multibyte string")); break; } #ifndef DISABLE_OPERATINGDIR if (strcasecmp(rcopts[i].name, "operatingdir") == 0) operating_dir = option; else #endif #ifndef DISABLE_WRAPJUSTIFY if (strcasecmp(rcopts[i].name, "fill") == 0) { if (!parse_num(option, &wrap_at)) { rcfile_error( N_("Requested fill size \"%s\" is invalid"), option); wrap_at = -CHARS_FROM_EOL; } else free(option); } else #endif #ifndef NANO_TINY if (strcasecmp(rcopts[i].name, "matchbrackets") == 0) { matchbrackets = option; if (has_blank_mbchars(matchbrackets)) { rcfile_error( N_("Non-blank characters required")); free(matchbrackets); matchbrackets = NULL; } } else if (strcasecmp(rcopts[i].name, "whitespace") == 0) { whitespace = option; if (mbstrlen(whitespace) != 2 || strlenpt(whitespace) != 2) { rcfile_error( N_("Two single-column characters required")); free(whitespace); whitespace = NULL; } else { whitespace_len[0] = parse_mbchar(whitespace, NULL, NULL); whitespace_len[1] = parse_mbchar(whitespace + whitespace_len[0], NULL, NULL); } } else #endif #ifndef DISABLE_JUSTIFY if (strcasecmp(rcopts[i].name, "punct") == 0) { punct = option; if (has_blank_mbchars(punct)) { rcfile_error( N_("Non-blank characters required")); free(punct); punct = NULL; } } else if (strcasecmp(rcopts[i].name, "brackets") == 0) { brackets = option; if (has_blank_mbchars(brackets)) { rcfile_error( N_("Non-blank characters required")); free(brackets); brackets = NULL; } } else if (strcasecmp(rcopts[i].name, "quotestr") == 0) quotestr = option; else #endif #ifndef NANO_TINY if (strcasecmp(rcopts[i].name, "backupdir") == 0) backup_dir = option; else #endif #ifndef DISABLE_SPELLER if (strcasecmp(rcopts[i].name, "speller") == 0) alt_speller = option; else #endif if (strcasecmp(rcopts[i].name, "tabsize") == 0) { if (!parse_num(option, &tabsize) || tabsize <= 0) { rcfile_error( N_("Requested tab size \"%s\" is invalid"), option); tabsize = -1; } else free(option); } else assert(FALSE); } #ifdef DEBUG fprintf(stderr, "flag = %ld\n", rcopts[i].flag); #endif } else if (rcopts[i].flag != 0) UNSET(rcopts[i].flag); else rcfile_error(N_("Cannot unset flag \"%s\""), rcopts[i].name); break; } } if (rcopts[i].name == NULL) rcfile_error(N_("Unknown flag \"%s\""), option); } #ifdef ENABLE_COLOR if (endsyntax != NULL && endcolor == NULL) rcfile_error(N_("Syntax \"%s\" has no color commands"), endsyntax->desc); #endif free(buf); fclose(rcstream); lineno = 0; check_vitals_mapped(); return; }
/* Parse the color string in the line at ptr, and add it to the current * file's associated colors. If icase is TRUE, treat the color string * as case insensitive. */ void parse_colors(char *ptr, bool icase) { short fg, bg; bool bright = FALSE, no_fgcolor = FALSE; char *fgstr; assert(ptr != NULL); if (syntaxes == NULL) { rcfile_error( N_("Cannot add a color command without a syntax command")); return; } if (*ptr == '\0') { rcfile_error(N_("Missing color name")); return; } fgstr = ptr; ptr = parse_next_word(ptr); if (strchr(fgstr, ',') != NULL) { char *bgcolorname; strtok(fgstr, ","); bgcolorname = strtok(NULL, ","); if (bgcolorname == NULL) { /* If we have a background color without a foreground color, * parse it properly. */ bgcolorname = fgstr + 1; no_fgcolor = TRUE; } if (strncasecmp(bgcolorname, "bright", 6) == 0) { rcfile_error( N_("Background color \"%s\" cannot be bright"), bgcolorname); return; } bg = color_to_short(bgcolorname, &bright); } else bg = -1; if (!no_fgcolor) { fg = color_to_short(fgstr, &bright); /* Don't try to parse screwed-up foreground colors. */ if (fg == -1) return; } else fg = -1; if (*ptr == '\0') { rcfile_error(N_("Missing regex string")); return; } /* Now for the fun part. Start adding regexes to individual strings * in the colorstrings array, woo! */ while (ptr != NULL && *ptr != '\0') { colortype *newcolor; /* The new color structure. */ bool cancelled = FALSE; /* The start expression was bad. */ bool expectend = FALSE; /* Do we expect an end= line? */ if (strncasecmp(ptr, "start=", 6) == 0) { ptr += 6; expectend = TRUE; } if (*ptr != '"') { rcfile_error( N_("Regex strings must begin and end with a \" character")); ptr = parse_next_regex(ptr); continue; } ptr++; fgstr = ptr; ptr = parse_next_regex(ptr); if (ptr == NULL) break; newcolor = (colortype *)nmalloc(sizeof(colortype)); /* Save the starting regex string if it's valid, and set up the * color information. */ if (nregcomp(fgstr, icase ? REG_ICASE : 0)) { newcolor->fg = fg; newcolor->bg = bg; newcolor->bright = bright; newcolor->icase = icase; newcolor->start_regex = mallocstrcpy(NULL, fgstr); newcolor->start = NULL; newcolor->end_regex = NULL; newcolor->end = NULL; newcolor->next = NULL; if (endcolor == NULL) { endsyntax->color = newcolor; #ifdef DEBUG fprintf(stderr, "Starting a new colorstring for fg %hd, bg %hd\n", fg, bg); #endif } else { #ifdef DEBUG fprintf(stderr, "Adding new entry for fg %hd, bg %hd\n", fg, bg); #endif endcolor->next = newcolor; } endcolor = newcolor; } else { free(newcolor); cancelled = TRUE; } if (expectend) { if (ptr == NULL || strncasecmp(ptr, "end=", 4) != 0) { rcfile_error( N_("\"start=\" requires a corresponding \"end=\"")); return; } ptr += 4; if (*ptr != '"') { rcfile_error( N_("Regex strings must begin and end with a \" character")); continue; } ptr++; fgstr = ptr; ptr = parse_next_regex(ptr); if (ptr == NULL) break; /* If the start regex was invalid, skip past the end regex to * stay in sync. */ if (cancelled) continue; /* Save the ending regex string if it's valid. */ newcolor->end_regex = (nregcomp(fgstr, icase ? REG_ICASE : 0)) ? mallocstrcpy(NULL, fgstr) : NULL; /* Lame way to skip another static counter */ newcolor->id = endsyntax->nmultis; endsyntax->nmultis++; } } }
void parse_keybinding(char *ptr) { char *keyptr = NULL, *keycopy = NULL, *funcptr = NULL, *menuptr = NULL; sc *s, *newsc; int i, menu; assert(ptr != NULL); if (*ptr == '\0') { rcfile_error(N_("Missing key name")); return; } keyptr = ptr; ptr = parse_next_word(ptr); keycopy = mallocstrcpy(NULL, keyptr); for (i = 0; i < strlen(keycopy); i++) keycopy[i] = toupper(keycopy[i]); if (keycopy[0] != 'M' && keycopy[0] != '^' && keycopy[0] != 'F' && keycopy[0] != 'K') { rcfile_error( N_("keybindings must begin with \"^\", \"M\", or \"F\"")); return; } funcptr = ptr; ptr = parse_next_word(ptr); if (!strcmp(funcptr, "")) { rcfile_error( N_("Must specify function to bind key to")); return; } menuptr = ptr; ptr = parse_next_word(ptr); if (!strcmp(menuptr, "")) { rcfile_error( /* Note to translators, do not translate the word "all" in the sentence below, everything else is fine */ N_("Must specify menu to bind key to (or \"all\")")); return; } newsc = strtosc(menu, funcptr); if (newsc == NULL) { rcfile_error( N_("Could not map name \"%s\" to a function"), funcptr); return; } menu = strtomenu(menuptr); if (menu < 1) { rcfile_error( N_("Could not map name \"%s\" to a menu"), menuptr); return; } #ifdef DEBUG fprintf(stderr, "newsc now address %d, menu func assigned = %d, menu = %d\n", &newsc, newsc->scfunc, menu); #endif newsc->keystr = keycopy; newsc->menu = menu; newsc->type = strtokeytype(newsc->keystr); assign_keyinfo(newsc); #ifdef DEBUG fprintf(stderr, "s->keystr = \"%s\"\n", newsc->keystr); fprintf(stderr, "s->seq = \"%d\"\n", newsc->seq); #endif if (check_bad_binding(newsc)) { rcfile_error( N_("Sorry, keystr \"%s\" is an illegal binding"), newsc->keystr); return; } /* now let's have some fun. Try and delete the other entries we found for the same menu, then make this new new beginning */ for (s = sclist; s != NULL; s = s->next) { if (((s->menu & newsc->menu)) && s->seq == newsc->seq) { s->menu &= ~newsc->menu; #ifdef DEBUG fprintf(stderr, "replaced menu entry %d\n", s->menu); #endif } } newsc->next = sclist; sclist = newsc; }
std::vector<command> parse_line() { line = ""; std::vector<command> pipes; std::vector<std::string> argvs; std::string argv, file1, file2; int file_num = 0, pipe_num = 1; int user_out = 0, user_in = 0; while(true) { char c = std::cin.get(); if(!std::cin.good() || std::cin.eof()) exit(0); if(c != '\n' && c != '\r') line.push_back(c); //if(c == '"') //{ // std::getline(std::cin, argv, '"'); // line += argv; //} //else if(c == '\'') //{ // std::getline(std::cin, argv, '\''); // line += argv; //} if(c == ' ' || c == '|' || c == '\n') { if(argv.size() != 0) { argv.erase(std::remove(argv.begin(), argv.end(), '\r'), argv.end()); argvs.push_back(argv); argv = ""; } } if(c == ' ' || c == '\r') {} else if(c == '/') { syntax_error(c); argv = ""; file1 = ""; file2 = ""; user_out = 0; user_in = 0; argvs.resize(0); pipes.resize(0); file_num=0; pipe_num=1; } else if(c == '|') { while(true) { char nc = std::cin.peek(); if(nc >= 48 && nc <= 57)// is 0~9 argv.push_back(std::cin.get()); else break; } pipe_num = std::atoi(argv.c_str()); if(pipe_num == 0) pipe_num = 1; pipes.push_back({pipe_num, 0, false, argvs, file1, file2, user_out, user_in}); line += argv; argv = ""; file1 = ""; file2 = ""; user_out = 0; user_in = 0; argvs.resize(0); pipe_num = 1; } else if(c == '>') { int file_num = 1; // ls >f, ls > f if(argv.size() != 0) { // ls 2> f, ls 2>f int num = std::atoi(argv.c_str()); if(num == 0) // ls>f argvs.push_back(argv); else file_num = num; } line += argv; argv = ""; int tag = parse_next_word(argv); if( tag == 0) { syntax_error(std::cin.peek()); pipes.resize(0); argvs.resize(0); } int to_user = atoi(argv.c_str()); if(tag == 1 || to_user == 0) // file { if(file_num == 1) file1 = argv; else file2 = argv; } else { user_out = to_user; } argv = ""; } else if(c == '<') { int file_num = 1; // ls >f, ls > f if(argv.size() != 0) { // ls 2> f, ls 2>f int num = std::atoi(argv.c_str()); if(num == 0) // ls>f argvs.push_back(argv); else file_num = num; } line += argv; argv = ""; int tag = parse_next_word(argv); if( tag == 0) { syntax_error(std::cin.peek()); pipes.resize(0); argvs.resize(0); } int to_user = atoi(argv.c_str()); if(tag == 1 || to_user == 0) // file {} else { user_in = to_user; } argv = ""; } else if(c == '\n') { if(argvs.size() > 0) pipes.push_back({pipe_num, 0, true, argvs, file1, file2, user_out, user_in}); break; } else argv.push_back(c); } return pipes; }
/* Parse the color string in the line at ptr, and add it to the current * file's associated colors. If icase is TRUE, treat the color string * as case insensitive. */ void parse_colors(char *ptr, bool icase) { short fg, bg; bool bright = FALSE; char *fgstr; assert(ptr != NULL); if (syntaxes == NULL) { rcfile_error( N_("Cannot add a color command without a syntax command")); return; } if (*ptr == '\0') { rcfile_error(N_("Missing color name")); return; } fgstr = ptr; ptr = parse_next_word(ptr); if (!parse_color_names(fgstr, &fg, &bg, &bright)) return; if (*ptr == '\0') { rcfile_error(N_("Missing regex string")); return; } /* Now for the fun part. Start adding regexes to individual strings * in the colorstrings array, woo! */ while (ptr != NULL && *ptr != '\0') { colortype *newcolor; /* The container for a color plus its regexes. */ bool cancelled = FALSE; /* The start expression was bad. */ bool expectend = FALSE; /* Do we expect an end= line? */ if (strncasecmp(ptr, "start=", 6) == 0) { ptr += 6; expectend = TRUE; } if (*ptr != '"') { rcfile_error( N_("Regex strings must begin and end with a \" character")); ptr = parse_next_regex(ptr); continue; } ptr++; fgstr = ptr; ptr = parse_next_regex(ptr); if (ptr == NULL) break; newcolor = (colortype *)nmalloc(sizeof(colortype)); /* Save the starting regex string if it's valid, and set up the * color information. */ if (nregcomp(fgstr, icase ? REG_ICASE : 0)) { newcolor->fg = fg; newcolor->bg = bg; newcolor->bright = bright; newcolor->icase = icase; newcolor->start_regex = mallocstrcpy(NULL, fgstr); newcolor->start = NULL; newcolor->end_regex = NULL; newcolor->end = NULL; newcolor->next = NULL; if (endcolor == NULL) { endsyntax->color = newcolor; #ifdef DEBUG fprintf(stderr, "Starting a new colorstring for fg %hd, bg %hd\n", fg, bg); #endif } else { #ifdef DEBUG fprintf(stderr, "Adding new entry for fg %hd, bg %hd\n", fg, bg); #endif /* Need to recompute endcolor now so we can extend * colors to syntaxes. */ for (endcolor = endsyntax->color; endcolor->next != NULL; endcolor = endcolor->next) ; endcolor->next = newcolor; } endcolor = newcolor; } else { free(newcolor); cancelled = TRUE; } if (expectend) { if (ptr == NULL || strncasecmp(ptr, "end=", 4) != 0) { rcfile_error( N_("\"start=\" requires a corresponding \"end=\"")); return; } ptr += 4; if (*ptr != '"') { rcfile_error( N_("Regex strings must begin and end with a \" character")); continue; } ptr++; fgstr = ptr; ptr = parse_next_regex(ptr); if (ptr == NULL) break; /* If the start regex was invalid, skip past the end regex * to stay in sync. */ if (cancelled) continue; /* Save the ending regex string if it's valid. */ newcolor->end_regex = (nregcomp(fgstr, icase ? REG_ICASE : 0)) ? mallocstrcpy(NULL, fgstr) : NULL; /* Lame way to skip another static counter. */ newcolor->id = endsyntax->nmultis; endsyntax->nmultis++; } } }
/* Let user unbind a sequence from a given (or all) menus */ void parse_unbinding(char *ptr) { char *keyptr = NULL, *keycopy = NULL, *menuptr = NULL; sc *s; int i, menu; assert(ptr != NULL); if (*ptr == '\0') { rcfile_error(N_("Missing key name")); return; } keyptr = ptr; ptr = parse_next_word(ptr); keycopy = mallocstrcpy(NULL, keyptr); for (i = 0; i < strlen(keycopy); i++) keycopy[i] = toupper(keycopy[i]); #ifdef DEBUG fprintf(stderr, "Starting unbinding code"); #endif if (keycopy[0] != 'M' && keycopy[0] != '^' && keycopy[0] != 'F' && keycopy[0] != 'K') { rcfile_error( N_("keybindings must begin with \"^\", \"M\", or \"F\"")); return; } menuptr = ptr; ptr = parse_next_word(ptr); if (!strcmp(menuptr, "")) { rcfile_error( /* Note to translators, do not translate the word "all" in the sentence below, everything else is fine */ N_("Must specify menu to bind key to (or \"all\")")); return; } menu = strtomenu(menuptr); if (menu < 1) { rcfile_error( N_("Could not map name \"%s\" to a menu"), menuptr); return; } #ifdef DEBUG fprintf(stderr, "unbinding \"%s\" from menu = %d\n", keycopy, menu); #endif /* Now find the apropriate entries in the menu to delete */ for (s = sclist; s != NULL; s = s->next) { if (((s->menu & menu)) && !strcmp(s->keystr,keycopy)) { s->menu &= ~menu; #ifdef DEBUG fprintf(stderr, "deleted menu entry %d\n", s->menu); #endif } } }
/* Bind or unbind a key combo, to or from a function. */ void parse_binding(char *ptr, bool dobind) { char *keyptr = NULL, *keycopy = NULL, *funcptr = NULL, *menuptr = NULL; sc *s, *newsc = NULL; int menu; assert(ptr != NULL); #ifdef DEBUG fprintf(stderr, "Starting the rebinding code...\n"); #endif if (*ptr == '\0') { rcfile_error(N_("Missing key name")); return; } keyptr = ptr; ptr = parse_next_word(ptr); keycopy = mallocstrcpy(NULL, keyptr); if (strlen(keycopy) < 2) { rcfile_error(N_("Key name is too short")); return; } /* Uppercase only the first two or three characters of the key name. */ keycopy[0] = toupper(keycopy[0]); keycopy[1] = toupper(keycopy[1]); if (keycopy[0] == 'M' && keycopy[1] == '-') { if (strlen(keycopy) > 2) keycopy[2] = toupper(keycopy[2]); else { rcfile_error(N_("Key name is too short")); return; } } /* Allow the codes for Insert and Delete to be rebound, but apart * from those two only Control, Meta and Function sequences. */ if (!strcasecmp(keycopy, "Ins") || !strcasecmp(keycopy, "Del")) keycopy[1] = tolower(keycopy[1]); else if (keycopy[0] != '^' && keycopy[0] != 'M' && keycopy[0] != 'F') { rcfile_error(N_("Key name must begin with \"^\", \"M\", or \"F\"")); return; } if (dobind) { funcptr = ptr; ptr = parse_next_word(ptr); if (funcptr[0] == '\0') { rcfile_error(N_("Must specify a function to bind the key to")); return; } } menuptr = ptr; ptr = parse_next_word(ptr); if (menuptr[0] == '\0') { /* TRANSLATORS: Do not translate the word "all". */ rcfile_error(N_("Must specify a menu (or \"all\") in which to bind/unbind the key")); return; } if (dobind) { newsc = strtosc(funcptr); if (newsc == NULL) { rcfile_error(N_("Cannot map name \"%s\" to a function"), funcptr); return; } } menu = strtomenu(menuptr); if (menu < 1) { rcfile_error(N_("Cannot map name \"%s\" to a menu"), menuptr); return; } #ifdef DEBUG if (dobind) fprintf(stderr, "newsc address is now %ld, assigned func = %ld, menu = %x\n", (long)&newsc, (long)newsc->scfunc, menu); else fprintf(stderr, "unbinding \"%s\" from menu %x\n", keycopy, menu); #endif if (dobind) { subnfunc *f; int mask = 0; /* Tally up the menus where the function exists. */ for (f = allfuncs; f != NULL; f = f->next) if (f->scfunc == newsc->scfunc) mask = mask | f->menus; /* Handle the special case of the toggles. */ if (newsc->scfunc == do_toggle_void) mask = MMAIN; /* Now limit the given menu to those where the function exists. */ if (is_universal(newsc->scfunc)) menu = menu & MMOST; else menu = menu & mask; if (!menu) { rcfile_error(N_("Function '%s' does not exist in menu '%s'"), funcptr, menuptr); free(newsc); return; } newsc->keystr = keycopy; newsc->menu = menu; newsc->type = strtokeytype(newsc->keystr); assign_keyinfo(newsc); #ifdef DEBUG fprintf(stderr, "s->keystr = \"%s\"\n", newsc->keystr); fprintf(stderr, "s->seq = \"%d\"\n", newsc->seq); #endif if (check_bad_binding(newsc)) { rcfile_error(N_("Sorry, keystroke \"%s\" may not be rebound"), newsc->keystr); free(newsc); return; } } /* Now find and delete any existing same shortcut in the menu(s). */ for (s = sclist; s != NULL; s = s->next) { if (((s->menu & menu)) && !strcmp(s->keystr, keycopy)) { #ifdef DEBUG fprintf(stderr, "deleting entry from menu %x\n", s->menu); #endif s->menu &= ~menu; } } if (dobind) { /* Add the new shortcut at the start of the list. */ newsc->next = sclist; sclist = newsc; } }
/* Parse the rcfile, once it has been opened successfully at rcstream, * and close it afterwards. If syntax_only is TRUE, only allow the file * to contain color syntax commands: syntax, color, and icolor. */ void parse_rcfile(FILE *rcstream #ifndef DISABLE_COLOR , bool syntax_only #endif ) { char *buf = NULL; ssize_t len; size_t n = 0; #ifndef DISABLE_COLOR syntaxtype *end_syn_save = NULL; #endif while ((len = getline(&buf, &n, rcstream)) > 0) { char *ptr, *keyword, *option; int set = 0; size_t i; /* Ignore the newline. */ if (buf[len - 1] == '\n') buf[len - 1] = '\0'; lineno++; ptr = buf; while (isblank(*ptr)) ptr++; /* If we have a blank line or a comment, skip to the next * line. */ if (*ptr == '\0' || *ptr == '#') continue; /* Otherwise, skip to the next space. */ keyword = ptr; ptr = parse_next_word(ptr); #ifndef DISABLE_COLOR /* Handle extending first... */ if (strcasecmp(keyword, "extendsyntax") == 0) { char *syntaxname = ptr; syntaxtype *ts = NULL; ptr = parse_next_word(ptr); for (ts = syntaxes; ts != NULL; ts = ts->next) if (!strcmp(ts->desc, syntaxname)) break; if (ts == NULL) { rcfile_error(N_("Could not find syntax \"%s\" to extend"), syntaxname); continue; } else { end_syn_save = endsyntax; endsyntax = ts; keyword = ptr; ptr = parse_next_word(ptr); } } #endif /* Try to parse the keyword. */ if (strcasecmp(keyword, "set") == 0) { #ifndef DISABLE_COLOR if (syntax_only) rcfile_error( N_("Command \"%s\" not allowed in included file"), keyword); else #endif set = 1; } else if (strcasecmp(keyword, "unset") == 0) { #ifndef DISABLE_COLOR if (syntax_only) rcfile_error( N_("Command \"%s\" not allowed in included file"), keyword); else #endif set = -1; } #ifndef DISABLE_COLOR else if (strcasecmp(keyword, "include") == 0) { if (syntax_only) rcfile_error( N_("Command \"%s\" not allowed in included file"), keyword); else parse_include(ptr); } else if (strcasecmp(keyword, "syntax") == 0) { if (endsyntax != NULL && endcolor == NULL) rcfile_error(N_("Syntax \"%s\" has no color commands"), endsyntax->desc); parse_syntax(ptr); } else if (strcasecmp(keyword, "magic") == 0) #ifdef HAVE_LIBMAGIC parse_magic_exp(ptr); #else ; #endif else if (strcasecmp(keyword, "header") == 0) parse_header_exp(ptr); else if (strcasecmp(keyword, "color") == 0) parse_colors(ptr, FALSE); else if (strcasecmp(keyword, "icolor") == 0) parse_colors(ptr, TRUE); else if (strcasecmp(keyword, "linter") == 0) parse_linter(ptr); else if (strcasecmp(keyword, "formatter") == 0) #ifndef DISABLE_SPELLER parse_formatter(ptr); #else ; #endif #endif /* !DISABLE_COLOR */ else if (strcasecmp(keyword, "bind") == 0)
/* Parse the rcfile, once it has been opened successfully at rcstream, * and close it afterwards. If syntax_only is TRUE, allow the file to * to contain only color syntax commands. */ void parse_rcfile(FILE *rcstream, bool syntax_only) { char *buf = NULL; ssize_t len; size_t n = 0; while ((len = getline(&buf, &n, rcstream)) > 0) { char *ptr, *keyword, *option; int set = 0; size_t i; /* Ignore the newline. */ if (buf[len - 1] == '\n') buf[len - 1] = '\0'; lineno++; ptr = buf; while (isblank(*ptr)) ptr++; /* If we have a blank line or a comment, skip to the next * line. */ if (*ptr == '\0' || *ptr == '#') continue; /* Otherwise, skip to the next space. */ keyword = ptr; ptr = parse_next_word(ptr); #ifndef DISABLE_COLOR /* Handle extending first... */ if (strcasecmp(keyword, "extendsyntax") == 0) { syntaxtype *sint; char *syntaxname = ptr; ptr = parse_next_word(ptr); for (sint = syntaxes; sint != NULL; sint = sint->next) if (!strcmp(sint->name, syntaxname)) break; if (sint == NULL) { rcfile_error(N_("Could not find syntax \"%s\" to extend"), syntaxname); opensyntax = FALSE; continue; } live_syntax = sint; opensyntax = TRUE; /* Refind the tail of the color list for this syntax. */ lastcolor = sint->color; if (lastcolor != NULL) while (lastcolor->next != NULL) lastcolor = lastcolor->next; keyword = ptr; ptr = parse_next_word(ptr); } /* Try to parse the keyword. */ if (strcasecmp(keyword, "syntax") == 0) { if (opensyntax && lastcolor == NULL) rcfile_error(N_("Syntax \"%s\" has no color commands"), live_syntax->name); parse_syntax(ptr); } else if (strcasecmp(keyword, "header") == 0) grab_and_store("header", ptr, &live_syntax->headers); else if (strcasecmp(keyword, "magic") == 0) #ifdef HAVE_LIBMAGIC grab_and_store("magic", ptr, &live_syntax->magics); #else ; #endif else if (strcasecmp(keyword, "comment") == 0) #ifdef ENABLE_COMMENT pick_up_name("comment", ptr, &live_syntax->comment); #else ; #endif else if (strcasecmp(keyword, "color") == 0)
/* Parse the color string in the line at ptr, and add it to the current * file's associated colors. rex_flags are the regex compilation flags * to use, excluding or including REG_ICASE for case (in)sensitivity. */ void parse_colors(char *ptr, int rex_flags) { short fg, bg; bool bright = FALSE; char *fgstr; assert(ptr != NULL); if (!opensyntax) { rcfile_error( N_("A '%s' command requires a preceding 'syntax' command"), "color"); return; } if (*ptr == '\0') { rcfile_error(N_("Missing color name")); return; } fgstr = ptr; ptr = parse_next_word(ptr); if (!parse_color_names(fgstr, &fg, &bg, &bright)) return; if (*ptr == '\0') { rcfile_error(N_("Missing regex string after '%s' command"), "color"); return; } /* Now for the fun part. Start adding regexes to individual strings * in the colorstrings array, woo! */ while (ptr != NULL && *ptr != '\0') { colortype *newcolor = NULL; /* The container for a color plus its regexes. */ bool goodstart; /* Whether the start expression was valid. */ bool expectend = FALSE; /* Whether to expect an end= line. */ if (strncasecmp(ptr, "start=", 6) == 0) { ptr += 6; expectend = TRUE; } if (*ptr != '"') { rcfile_error( N_("Regex strings must begin and end with a \" character")); ptr = parse_next_regex(ptr); continue; } fgstr = ++ptr; ptr = parse_next_regex(ptr); if (ptr == NULL) break; goodstart = nregcomp(fgstr, rex_flags); /* If the starting regex is valid, initialize a new color struct, * and hook it in at the tail of the linked list. */ if (goodstart) { newcolor = (colortype *)nmalloc(sizeof(colortype)); newcolor->fg = fg; newcolor->bg = bg; newcolor->bright = bright; newcolor->rex_flags = rex_flags; newcolor->start_regex = mallocstrcpy(NULL, fgstr); newcolor->start = NULL; newcolor->end_regex = NULL; newcolor->end = NULL; newcolor->next = NULL; #ifdef DEBUG fprintf(stderr, "Adding an entry for fg %hd, bg %hd\n", fg, bg); #endif if (lastcolor == NULL) live_syntax->color = newcolor; else lastcolor->next = newcolor; lastcolor = newcolor; } if (!expectend) continue; if (ptr == NULL || strncasecmp(ptr, "end=", 4) != 0) { rcfile_error(N_("\"start=\" requires a corresponding \"end=\"")); return; } ptr += 4; if (*ptr != '"') { rcfile_error(N_("Regex strings must begin and end with a \" character")); continue; } fgstr = ++ptr; ptr = parse_next_regex(ptr); if (ptr == NULL) break; /* If the start regex was invalid, skip past the end regex * to stay in sync. */ if (!goodstart) continue; /* If it's valid, save the ending regex string. */ if (nregcomp(fgstr, rex_flags)) newcolor->end_regex = mallocstrcpy(NULL, fgstr); /* Lame way to skip another static counter. */ newcolor->id = live_syntax->nmultis; live_syntax->nmultis++; } }
/* Parse the next syntax name and its possible extension regexes from the * line at ptr, and add it to the global linked list of color syntaxes. */ void parse_syntax(char *ptr) { char *nameptr; /* A pointer to what should be the name of the syntax. */ opensyntax = FALSE; assert(ptr != NULL); /* Check that the syntax name is not empty. */ if (*ptr == '\0' || (*ptr == '"' && (*(ptr + 1) == '\0' || *(ptr + 1) == '"'))) { rcfile_error(N_("Missing syntax name")); return; } nameptr = ++ptr; ptr = parse_next_word(ptr); /* Check that the name starts and ends with a double quote. */ if (*(nameptr - 1) != '\x22' || nameptr[strlen(nameptr) - 1] != '\x22') { rcfile_error(N_("A syntax name must be quoted")); return; } /* Strip the end quote. */ nameptr[strlen(nameptr) - 1] = '\0'; /* Redefining the "none" syntax is not allowed. */ if (strcmp(nameptr, "none") == 0) { rcfile_error(N_("The \"none\" syntax is reserved")); return; } /* Initialize a new syntax struct. */ live_syntax = (syntaxtype *)nmalloc(sizeof(syntaxtype)); live_syntax->name = mallocstrcpy(NULL, nameptr); live_syntax->extensions = NULL; live_syntax->headers = NULL; live_syntax->magics = NULL; live_syntax->linter = NULL; live_syntax->formatter = NULL; live_syntax->comment = NULL; live_syntax->color = NULL; lastcolor = NULL; live_syntax->nmultis = 0; /* Hook the new syntax in at the top of the list. */ live_syntax->next = syntaxes; syntaxes = live_syntax; opensyntax = TRUE; #ifdef DEBUG fprintf(stderr, "Starting a new syntax type: \"%s\"\n", nameptr); #endif /* The default syntax should have no associated extensions. */ if (strcmp(live_syntax->name, "default") == 0 && *ptr != '\0') { rcfile_error( N_("The \"default\" syntax does not accept extensions")); return; } /* If there seem to be extension regexes, pick them up. */ if (*ptr != '\0') grab_and_store("extension", ptr, &live_syntax->extensions); }