ftp.nice.ch/pub/next/unix/text/rtf.N.bsd.tar.gz#/rtf-utilities/rtfcast.c

This is rtfcast.c in view mode; [Download] [Up]

/*
 * rtfcast template.rtf [files...]
 *
 * A macro processor which casts a "keyed" input
 * file through a template file (typically an RTF file).
 * The input looks like
 *     key: text...
 * A "key" is a string of [a-zA-Z0-9_] followed by a colon (':').
 * Occurrences of "$key" in the template file will be replaced
 * with that key's contents in the output.
 *
 * If input is stdin, output is stdout, otherwise the output
 * files are named <file>.<suffix>, where <suffix> is "rtf" (or
 * whatever the suffix of the template is).
 *
 * A multi-line macro is made by specifying a key: with no contents;
 * contents will be appended until a line containing only the key 
 * is seen again (or end-of-file, whicever comes first), eg:
 *     key:
 *     stuff...
 *     more stuff...
 *     key: (this ends the macro)
 *
 * Additionally, a backslash will continue a macro definition across
 * more than one line (though in this case the trailing newlines
 * will be stripped):
 *     key: stuff ...\
 *     morestuff...
 *     nextkey: ...
 * 
 * "RTFD" files -- rich-text files with attachments bundled into
 * a directory -- are properly handled *except* when reading from
 * the standard input (in which case only the RTF part, sans attachments,
 * will be written to the standard output).
 * To various extents, this program will work with other
 * text templates, like PostScript.
 *
 * This formatter currently lacks conventions for conditional expansion
 * (e.g., there is not yet an "if..." construct).
 *
 * Michael Hawley
 * MIT Media Laboratory
 * 20 Ames Street
 * Cambridge, MA 02139
 * mike@media-lab.mit.edu
 *
 * Copyright (c) MIT Media Laboratory, August 1991.
 *
 */

#include <stdio.h>
#define Case break; case
#define Default break; default
static char *_arg, *_argp; /* use by 'for_each_argument */
static char *av0;       /* will hold name of the command */
#define argument  (_arg=(*_argp? _argp : av[++i==ac? --i : i]),_argp+=strlen(_argp),_arg)
#define for_each_argument av0 = av[0]; for (i=1;i<ac && *av[i]=='-';i++)\
                        for (_argp = &av[i][1]; *_argp;)\
                                switch(*_argp++)
char *malloc(), *index();

typedef struct {
    char *key, *content;
} Pair;

#define MaxPairs 4096
Pair P[MaxPairs];
int NP = 0;
int DropUndefinedKeys = 1;

#include <sys/types.h>
#include <sys/stat.h>

int fdSize(char *f){ /* return size of file 'f' */
    struct stat b;
    stat(f, &b);
    return b.st_size;
}

int isDirectory(char *f){ /* true if file 'f' is a directory */
    struct stat b;
    stat(f, &b);
    return b.st_mode & S_IFDIR;
}

int error(a,b,c,d) int a,b,c,d; { /* printf an error msg */
    fprintf(stderr,(char *)a,b,c,d); fprintf(stderr,"\n");
    return 0;
}

int System(fmt, a,b,c,d,e,f,g)
    char *fmt,*a,*b,*c,*d,*e,*f,*g;
/*
 * "printf" a system call, gripe if it failed.
 */
{
    extern int system(char *s);
    char t[2048];
    int i;
    sprintf(t,fmt,a,b,c,d,e,f,g);
    i = system(t);
    if (i) error("bad command: %s",t);
    return !i;
}

char *
save(s) char *s; { /* save a copy of 's' and return the pointer */
    char *t = malloc(strlen(s)+1);
    if (t) strcpy(t,s);
    return t;
}

#define Free(s) if (s) free(s); s = (char *)0

resetP(){ /* free the macro pairs in 'P' */
    int i;
    for (i=0;i<NP;i++){
        Free(P[i].key);
        Free(P[i].content);
    }
    NP=0;
}

    int isRTFD = 0;
    char *templateSuffix = "rtf";

    char *
    readTemplate(s) char *s; {
	char *t = (char *)0;
	int f, n = fdSize(s);

	if (n<=0) return error("couldn't read template '%s'",s), t;
	t = malloc(n);
	if (!t) return error("no memory!"), t;
	f = open(s,0);
	if (f <= 0) return error("couldn't read template '%s'",s), (char *)0;
	if (read(f,t,n) < n)
	    error("warning -- possible missing data in template '%s'",s);
	close(f);
	if (strncmp(t,"{\\rtf",5)==0 && !isRTFD)
	    templateSuffix = "rtf";
	return t;
    }
    
char *
ReadTemplate(s)
    char *s;
/*
 * Read the template file (typically an RTF file)
 * and return it as a null-terminated string.
 * (Assumes that the file does not contain '\0' chars.)
 * If 's' is an rtfd bundle, following the NeXT convention,
 * look inside for the TXT.rtf.
 */
{
    char t[1024];

    strcpy(t,s);
    if (access(t,0)){
        sprintf(t,"%s/Library/Templates/%s",getenv("HOME"),s);
        if (access(t,0)){
            sprintf(t,"/LocalLibrary/Templates/%s",s);
            if (access(t,0))
                return error("couldn't find template '%s'",s), (char *)0;
        }
    }
    if (isDirectory(t)){ /* accommodate the NeXT .rtfd convention */
        strcat(t,"/TXT.rtf");
        isRTFD=1;
        templateSuffix = "rtfd";
    } else {
        char *p, *rindex();
        p = rindex(t,'.');
        if (p) templateSuffix = save(p+1);
        else templateSuffix = "cast";
    }
    return readTemplate(t);
}

#define space(s) (*s==' ' /* || *s=='\t' */ || *s=='\n')

    char *
    markcontent(s) char *s; {
	while (*s && *s != ':') ++s;
	if (*s==':'){
	    *s++ = '\0';
	    while (space(s)) ++s;
	    return s;
	}
	return (char *)0;
    }
    
    continues(s) char *s; {
	char *p = s + strlen(s)-1;
	return (p>s && p[-1]=='\\');
    }
    
    trim(s) char *s; {
	char *p = s + strlen(s)-1;
	if (*p=='\n'){
	    *p-- = '\0';
	    if (*p=='\\') *p-- = '\n';
	}
    }
    
    char *
    append(s,t) char *s,*t; {
	char *p = malloc(strlen(s)+strlen(t)+1);
	if (p){
	    strcpy(p,s);
	    strcat(p,t);
	    free(s);
	}
	return p;
    }
    
    kcmp(s,key) char *s, *key; {
	while (*s == *key && *s && *key)
	    ++s, ++key;
	return !(!*key && *s == ':');
    }

loadKeyfile(f)
    FILE *f;
/*
 * Load 'f' and make the appropriate macro bindings.
 */
{
    char s[64000], *c;
    int continuing=0;

    while (fgets(s,sizeof(s),f)){
        c = markcontent(s);
        if (!c) continue;
        P[NP].key = save(s);
        P[NP].content = save(c);
        trim(P[NP].content);
        if (!*c || continues(c)){
            if (continuing = continues(c)){
                while (continuing && fgets(s,sizeof(s),f)){
                    continuing = continues(s);
                    trim(s);
                    P[NP].content = append(P[NP].content,s);
                }
            } else {
                while (fgets(s,sizeof(s),f) && kcmp(s,P[NP].key))
                    P[NP].content = append(P[NP].content,s);
            }
        }
        NP++;
    }
}

#include <ctype.h>

    copyKey(s,t,n) char *s,*t; {
	while (--n && (isalnum(*s) || *s=='_' || *s=='-')) *t++ = *s++;
	*t = '\0';
    }
    
    char *
    isKey(s) char *s; { /* return the contents of the given key 's', or null */
	int i;
	for (i=0;i<NP;i++)
	    if (strcmp(s,P[i].key)==0) return P[i].content;
	return 0;
    }

    writenl(fd,s) char *s; { /* backslashify the newlines (for RTF files) */
	char *pp=s,*p=s;

        while (*p){
	    p = index(p,'\n');
	    if (!p || strncmp(templateSuffix,"rtf"))
                return write(fd,pp,strlen(pp));
	    write(fd,pp,p-pp);
	    write(fd,"\\\n",2);
	    p++;
	    pp=p;
	}
    }

    endsline(s) char *s;
    /*
     * Try to eliminate lines that refer to undefined macros.
     */
    {
        int failed = !DropUndefinedKeys;
        while (!failed && *s && *s != '\n'){
            if (*s=='\\' && isalpha(s[1])){ /* skip rtf directions */
                s++;
                while (*s && *s != ' ') s++;
                s++;
            } else {
                if (isalnum(*s) && *s != '\\') failed++;
                s++;
            }
        }
        if (!failed && s[0] == '\n' && s[-1]=='\\'){
            s[-1] = ' ';
            return 1;
        }
        return 0;
    }

expand(s,f)
    char *s;
    FILE *f;
/*
 * Copy 's' onto 'f', filling in any macros defined in 'P'.
 */
{
    char *pp=s, *p = s, key[512], *c;
    int fd = fileno(f);

    while (*p){
        p = index(p,'$');
        if (!p) return write(fd,pp,strlen(pp));
        copyKey(p+1,key,sizeof(key)-1);
        c = isKey(key);
        if (c){
            write(fd,pp,p-pp);
            writenl(fd,c);
            p += strlen(key) + 1;
            pp = p;
        } else {
            if (*key && isalpha(*key) && DropUndefinedKeys){
              if (endsline(p+strlen(key)+1)){
                write(fd,pp,p-pp);
                p += strlen(key)+1;
                while (*p==' ') ++p;
              } else {
                write(fd,pp,p-pp);
                p += strlen(key)+1;
              }
            } else {
                p++;
                write(fd,pp,p-pp);
            }
            pp=p;
        }
    }
}

use(){
    error("use: %s [-d] template.rtf [files or stdin]",av0);
    error("  -d: if $... is found in the template but is not a known key,");
    error("      do NOT drop it -- copy the $... to the output.");
    exit(1);
}

main(ac,av) char *av[]; {
    int i;
    char *t, *template;

    for_each_argument {
    case 'd': DropUndefinedKeys = !DropUndefinedKeys;
    Default: use();
    }

    if (i==ac) use();

    t = ReadTemplate(template=av[i++]);
    if (!t) exit(1);

    if (i==ac){
        loadKeyfile(stdin);
        expand(t,stdout);   /* NB: if template is RTFD, the attachments
                                   may be dropped; only a simple RTF stream
                                   can be written to stdout, not a .rtfd
                                   directory
                             */
    } else
    while (i<ac){
        FILE *f, *o;
        char out[1024];
        if (f = fopen(av[i],"r")){
            loadKeyfile(f);
            sprintf(out,"%s.%s",av[i],templateSuffix);
            if (strcmp(out,av[i])==0){
                error("%s: input/output have the same name! (skipping)",out);
                goto Close;
            }
            if (isRTFD){
                System("rm -rf %s; cp -r %s %s",out,template,out);
                strcat(out,"/TXT.rtf");
                unlink(out);
            }
                
            if (o = fopen(out,"w")){
                expand(t,o);
                fclose(o);
            } else
                error("couldn't write '%s'",out);
          Close:
            fclose(f);
            resetP();
        } else
            error("couldn't read '%s'",av[i]);
        i++;
    }
}

These are the contents of the former NiCE NeXT User Group NeXTSTEP/OpenStep software archive, currently hosted by Netfuture.ch.