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.