ack/util/ack/util.c
George Koehler 07b04c64c8 Let ack(1) compile files with non-ASCII names.
This commit changes how ack(1) parses backslashes in its descr files.
Before this commit, ack set the high bit of each character escaped by
a backslash, and later cleared all high bits in command arguments, but
this lost the high bits in non-ASCII filenames.  After this commit,
ack keeps backslashes in strings while processing them.  Functions
scanvars(), scanexpr(), doassign(), unravel(), addargs() now
understand backslashes.  I remove from ack_basename() the warning
about non-ASCII characters.

This commit makes some incompatible changes for backslashes in descr
files.  None of our descr files uses backslashes, except for those
backslashes that continue lines, and there are no changes for those
backslashes.  The problem with non-ASCII filenames had its cause in a
feature that we weren't using.

With this commit, ack now understands backslashes after the = sign in
both "var NAME=value" and "mapflag -flag NAME=value".  Before, ack
never scanned backslashes in "var" lines, so "var A=\{B}" failed to
prevent expansion of B.  Now it does.  Before, ack did scan for
backslashes in the "-flag NAME=" part of "mapflag" lines.  Now it
doesn't, so it is no longer possible to map a flag that contains a
literal space, tab, or star "*".

I removed the expansion of "{{" to "{".  One can use "\{" for a
literal "{", and "\{" now works in "var" lines.  Before and now, ack
never expanded "{" in flags for "mapflag", so the correct way to map a
literal flag "-{" remains "mapflag -{ ...", not "mapflag -{{ ...".
(The other way "mapflag -\{ ..." stops working with this commit.)

Backslashes in strange places, like "{NA\ME}", probably have different
behavior now.

Backslashes in "program" lines now work.  Before, ack scanned for
backslashes there but forgot to clear the high bits later.

Escaping < or > as \< or \> now works, and prevents substitution of
the input or output file paths.  Before, ack only expanded the first <
or > in each argument.  Now, it expands every unescaped < or > in an
argument, but this is an accident of how I rewrote the code.  I don't
suggest to put more than one each of < or > in a command.  The code no
longer optimizes away its recursive calls when the argument is "<".

The code continues to set or clear the high bit NO_SCAN on the first
characters of flags.  This doesn't seem to be a problem, because flags
usually begin with an ASCII hyphen '-'.
2016-11-15 11:33:35 -05:00

168 lines
3.6 KiB
C

/*
* (c) copyright 1987 by the Vrije Universiteit, Amsterdam, The Netherlands.
* See the copyright notice in the ACK home directory, in the file "Copyright".
*
*/
/**********************************************************************/
/* */
/* Several utility routines used throughout ack */
/* error handling, string handling and such. */
/* */
/**********************************************************************/
#include <stdlib.h>
#include <stdio.h>
#include <stdarg.h>
#include <string.h>
#include <ctype.h>
#include "ack.h"
#ifndef NORCSID
static char rcs_id[] = "$Id$" ;
#endif
extern char *progname ;
extern int w_flag ;
extern int n_error;
#ifdef DEBUG
# define STDOUT stdout
#else
# define STDOUT stderr
#endif
char *ack_basename(const char *string) {
static char retval[256] ;
const char *last_dot, *last_start, *fetch ;
char *store ;
int ctoken ;
last_dot= (char *)0 ;
last_start= string ;
for ( fetch=string ; ; fetch++ ) {
switch ( ctoken= *fetch&0377 ) {
case SUFCHAR : last_dot=fetch ; break ;
case '/' : last_start=fetch+1 ; break ;
case 0 : goto out ;
}
}
out:
if ( ! *last_start ) fuerror("empty filename \"%s\"",string) ;
for ( fetch= last_start, store=retval ;
*fetch && fetch!=last_dot && store< &retval[sizeof retval-1] ;
fetch++, store++ ) {
*store= *fetch ;
}
*store= 0 ;
return retval ;
}
char *skipblank(char *str) {
register char *ptr ;
for ( ptr=str ; *ptr==SPACE || *ptr==TAB ; ptr++ ) ;
return ptr ;
}
char *firstblank(char *str) {
register char *ptr ;
for ( ptr=str ; *ptr && *ptr!=SPACE && *ptr!=TAB ; ptr++ ) ;
return ptr ;
}
/* VARARGS1 */
void fatal(const char* fmt, ...)
{
/* Fatal internal error */
va_list ap;
va_start(ap, fmt);
fprintf(STDOUT,"%s: fatal internal error, ",progname) ;
vfprintf(STDOUT, fmt, ap);
fprintf(STDOUT,"\n") ;
quit(-2) ;
}
/* VARARGS1 */
void vprint(const char* fmt, ...)
{
/* Diagnostic print, no auto NL */
va_list ap;
va_start(ap, fmt);
vfprintf(STDOUT, fmt, ap);
va_end(ap);
}
/* VARARGS1 */
void fuerror(const char *fmt, ...) {
/* Fatal user error */
va_list ap;
va_start(ap, fmt);
fprintf(STDOUT,"%s: ",progname) ;
vfprintf(STDOUT, fmt, ap);
fprintf(STDOUT,"\n") ;
quit(-1) ;
}
/* VARARGS1 */
void werror(const char *fmt, ...) {
/* Warning user error, w_flag */
va_list ap;
if ( w_flag ) return ;
va_start(ap, fmt);
fprintf(STDOUT,"%s: warning, ",progname) ;
vfprintf(STDOUT, fmt, ap);
fprintf(STDOUT,"\n") ;
va_end(ap);
}
/* VARARGS1 */
void error(const char *fmt, ...) {
/* User error, it is the callers responsibility to quit */
va_list ap;
va_start(ap, fmt);
fprintf(STDOUT,"%s: ",progname) ;
vfprintf(STDOUT, fmt, ap);
fprintf(STDOUT,"\n") ;
n_error++ ;
va_end(ap);
}
void quit(int code) {
rmtemps();
exit(code);
}
/******
char *keeps(string)
Keep the string in stable storage.
throws(string)
Remove the string stored by keep from stable storage.
throws() is now a macro in ack.h.
***********/
char *keeps(const char *str) {
register char *result ;
result= getcore( (unsigned)(strlen(str)+1) ) ;
if ( !result ) fatal("Out of core") ;
return strcpy(result,str) ;
}
void *getcore(size_t size) {
void *retptr ;
retptr= calloc(1,size) ;
if ( !retptr ) fatal("Out of memory") ;
return retptr ;
}
void *changecore(void *ptr, size_t size) {
void *retptr ;
retptr= realloc(ptr,size) ;
if ( !retptr ) fatal("Out of memory") ;
return retptr ;
}