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.