+2008-09-21 Chris Allegretta <chrisa@asty.org>
+ * rcfile.c, color.c, nano.h: Add new capability for matching a syntax type by the "header" (1st line)
+ of a file being edited. Based on Savannah bug 24197 and inital proof of concept by Dave Geering
+ <dgeering@toshiba-tap.com>
2008-09-16 Chris Allegretta <chrisa@asty.org>
* text.c: Add support for undoing a text uncut. Split out the undo and redo of a text cut
in order to avoid code duplication.
icolor brightwhite "^[[:space:]]*((un)?set|include|syntax|i?color).*$"
## Keywords
icolor brightgreen "^[[:space:]]*(set|unset)[[:space:]]+(autoindent|backup|backupdir|backwards|boldtext|brackets|casesensitive|const|cut|fill|historylog|matchbrackets|morespace|mouse|multibuffer|noconvert|nofollow|nohelp|nonewlines|nowrap|operatingdir|preserve|punct)\>" "^[[:space:]]*(set|unset)[[:space:]]+(quickblank|quotestr|rebinddelete|rebindkeypad|regexp|smarthome|smooth|speller|suspend|tabsize|tabstospaces|tempfile|view|whitespace|wordbounds)\>"
-icolor green "^[[:space:]]*(set|unset|include|syntax)\>"
+icolor green "^[[:space:]]*(set|unset|include|syntax|header)\>"
## Colors
icolor yellow "^[[:space:]]*i?color[[:space:]]*(bright)?(white|black|red|blue|green|yellow|magenta|cyan)?(,(white|black|red|blue|green|yellow|magenta|cyan))?\>"
icolor magenta "^[[:space:]]*i?color\>" "\<(start|end)="
## Here is an example for Perl.
##
syntax "perl" "\.p[lm]$"
+header "^#!.*/perl[-0-9._]*"
color red "\<(accept|alarm|atan2|bin(d|mode)|c(aller|h(dir|mod|op|own|root)|lose(dir)?|onnect|os|rypt)|d(bm(close|open)|efined|elete|ie|o|ump)|e(ach|of|val|x(ec|ists|it|p))|f(cntl|ileno|lock|ork))\>" "\<(get(c|login|peername|pgrp|ppid|priority|pwnam|(host|net|proto|serv)byname|pwuid|grgid|(host|net)byaddr|protobynumber|servbyport)|([gs]et|end)(pw|gr|host|net|proto|serv)ent|getsock(name|opt)|gmtime|goto|grep|hex|index|int|ioctl|join)\>" "\<(keys|kill|last|length|link|listen|local(time)?|log|lstat|m|mkdir|msg(ctl|get|snd|rcv)|next|oct|open(dir)?|ord|pack|pipe|pop|printf?|push|q|qq|qx|rand|re(ad(dir|link)?|cv|do|name|quire|set|turn|verse|winddir)|rindex|rmdir|s|scalar|seek(dir)?)\>" "\<(se(lect|mctl|mget|mop|nd|tpgrp|tpriority|tsockopt)|shift|shm(ctl|get|read|write)|shutdown|sin|sleep|socket(pair)?|sort|spli(ce|t)|sprintf|sqrt|srand|stat|study|substr|symlink|sys(call|read|tem|write)|tell(dir)?|time|tr(y)?|truncate|umask)\>" "\<(un(def|link|pack|shift)|utime|values|vec|wait(pid)?|wantarray|warn|write)\>"
color magenta "\<(continue|else|elsif|do|for|foreach|if|unless|until|while|eq|ne|lt|gt|le|ge|cmp|x|my|sub|use|package|can|isa)\>"
icolor cyan start="[$@%]" end="( |[^0-9A-Z_]|-)"
## Here is an example for Bourne shell scripts.
##
syntax "sh" "\.sh$"
+header "^#!.*/(ba|k|pdk|)sh[-0-9_]*"
icolor brightgreen "^[0-9A-Z_]+\(\)"
color green "\<(case|do|done|elif|else|esac|exit|fi|for|function|if|in|local|read|return|select|shift|then|time|until|while)\>"
color green "(\{|\}|\(|\)|\;|\]|\[|`|\\|\$|<|>|!|=|&|\|)"
/* If we didn't specify a syntax override string, or if we did and
* there was no syntax by that name, get the syntax based on the
- * file extension. */
+ * file extension, and then look in the header. */
if (openfile->colorstrings == NULL) {
for (tmpsyntax = syntaxes; tmpsyntax != NULL;
tmpsyntax = tmpsyntax->next) {
}
}
}
+
+ /* If we haven't matched anything yet, try the headers */
+ if (openfile->colorstrings == NULL) {
+#ifdef DEBUG
+ fprintf(stderr, "No match for file extensions, looking at headers...\n");
+#endif
+ for (tmpsyntax = syntaxes; tmpsyntax != NULL;
+ tmpsyntax = tmpsyntax->next) {
+ exttype *e;
+
+ for (e = tmpsyntax->headers; e != NULL; e = e->next) {
+ bool not_compiled = (e->ext == NULL);
+
+ /* e->ext_regex has already been checked for validity
+ * elsewhere. Compile its specified regex if we haven't
+ * already. */
+ if (not_compiled) {
+ e->ext = (regex_t *)nmalloc(sizeof(regex_t));
+ regcomp(e->ext, fixbounds(e->ext_regex), REG_EXTENDED);
+ }
+
+ /* Set colorstrings if we matched the extension
+ * regex. */
+#ifdef DEBUG
+ fprintf(stderr, "Comparing header regex \"%s\" to fileage \"%s\"...\n", e->ext_regex, openfile->fileage->data);
+#endif
+ if (regexec(e->ext, openfile->fileage->data, 0, NULL, 0) == 0)
+ openfile->colorstrings = tmpsyntax->color;
+
+ if (openfile->colorstrings != NULL)
+ break;
+
+ /* Decompile e->ext_regex's specified regex if we aren't
+ * going to use it. */
+ if (not_compiled) {
+ regfree(e->ext);
+ free(e->ext);
+ e->ext = NULL;
+ }
+ }
+ }
+ }
}
+
/* If we didn't get a syntax based on the file extension, and we
* have a default syntax, use it. */
if (openfile->colorstrings == NULL && defcolor != NULL)
/* The name of this syntax. */
exttype *extensions;
/* The list of extensions that this syntax applies to. */
+ exttype *headers;
+ /* Regexes to match on the 'header' (1st line) of the file */
colortype *color;
/* The colors used in this syntax. */
struct syntaxtype *next;
#ifdef ENABLE_COLOR
static syntaxtype *endsyntax = NULL;
/* The end of the list of syntaxes. */
+static exttype *endheader = NULL;
+ /* End of header list */
static colortype *endcolor = NULL;
/* The end of the color list for the current syntax. */
#endif
endsyntax->desc = mallocstrcpy(NULL, nameptr);
endsyntax->color = NULL;
endcolor = NULL;
+ endheader = NULL;
endsyntax->extensions = NULL;
+ endsyntax->headers = NULL;
endsyntax->next = NULL;
#ifdef DEBUG
}
}
}
+
+/* Parse the headers (1st line) of the file which may influence the regex used. */
+void parse_headers(char *ptr)
+{
+ char *h, *regstr;
+
+ assert(ptr != NULL);
+
+ if (syntaxes == NULL) {
+ rcfile_error(
+ N_("Cannot add a header regex without a syntax command"));
+ 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') {
+ exttype *newheader;
+ /* The new color structure. */
+ bool cancelled = FALSE;
+ /* The start expression was bad. */
+
+ if (*ptr != '"') {
+ rcfile_error(
+ N_("Regex strings must begin and end with a \" character"));
+ ptr = parse_next_regex(ptr);
+ continue;
+ }
+
+ ptr++;
+
+ regstr = ptr;
+ ptr = parse_next_regex(ptr);
+ if (ptr == NULL)
+ break;
+
+ newheader = (exttype *)nmalloc(sizeof(exttype));
+
+ /* Save the regex string if it's valid */
+ if (nregcomp(regstr, 0)) {
+ newheader->ext_regex = mallocstrcpy(NULL, regstr);
+ newheader->ext = NULL;
+ newheader->next = NULL;
+
+#ifdef DEBUG
+ fprintf(stderr, "Starting a new header entry: %s\n", newheader->ext_regex);
+#endif
+
+ if (endheader == NULL) {
+ endsyntax->headers = newheader;
+ } else {
+ endheader->next = newheader;
+ }
+
+ endheader = newheader;
+ } else
+ free(newheader);
+
+ }
+}
#endif /* ENABLE_COLOR */
/* Check whether the user has unmapped every shortcut for a
rcfile_error(N_("Syntax \"%s\" has no color commands"),
endsyntax->desc);
parse_syntax(ptr);
- } else if (strcasecmp(keyword, "color") == 0)
+ } 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);