From decwrl!ucbvax!tut.cis.ohio-state.edu!cs.utexas.edu!uunet!allbery Sat Mar 10 15:34:40 PST 1990 Article 1391 of comp.sources.misc: Path: decwrl!ucbvax!tut.cis.ohio-state.edu!cs.utexas.edu!uunet!allbery From: peter@ficc.uu.net (Peter da Silva) Newsgroups: comp.sources.misc Subject: v11i016: Property list files... Message-ID: <80856@uunet.UU.NET> Date: 10 Mar 90 19:49:01 GMT Sender: allbery@uunet.UU.NET Organization: Xenix Support, FICC Lines: 944 Approved: allbery@uunet.UU.NET (Brandon S. Allbery - comp.sources.misc) Posting-number: Volume 11, Issue 16 Submitted-by: peter@ficc.uu.net (Peter da Silva) Archive-name: plist/part01 Handy routines & programs for manipulating files with a 'property list' header containing a bunch of variables, such as news articles, mail messages, many makefiles, etc... #! /bin/sh # This is a shell archive. Remove anything before this line, then unpack # it by saving it into a file and typing "sh file". To overwrite existing # files, type "sh file -c". You can also feed this as standard input via # unshar, or by typing "sh 'Makefile' <<'END_OF_FILE' X# X# If your MAKE is broken, leave this line in: X# XSHELL=/bin/sh X# X# If you don't have a RENAME system call: X# XRENAME=-DRENAME X# X# How to optimise stuff: X# XOPT=-O -i X# X# Places to install stuff X# XMANDIR=//xds13/usr1/local/man XBINDIR=/usr/xds/bin X# X# Leave the rest alone X# XPROPOBJS= main.o prop.o XPROPSRCS= prop.h main.c prop.c XCFLAGS= $(OPT) $(RENAME) XMANPAGES= prop.1.nr prop.3.nr XSHARFILES= Makefile $(MANPAGES) $(PROPSRCS) XALLTARGETS= prop prop.1 prop.3 prop.shar X Xtest: prop X -rm putprop getprop remprop X ln prop putprop X ln prop getprop X ln prop remprop X Xprop: $(PROPOBJS) Makefile X $(CC) $(CFLAGS) $(PROPOBJS) -o prop X Xprop.1: prop.1.nr X nroff -man prop.1.nr | col > prop.1 X Xprop.3: prop.3.nr X nroff -man prop.3.nr | col > prop.3 X Xprint: Makefile $(PROPSRCS) prop.1 prop.3 X cpr -r Makefile $(PROPSRCS) | strike | npr X strike prop.1 prop.3 | npr X Xprop.shar: $(SHARFILES) X shar $(SHARFILES) > prop.shar X Xinstall: prop prop.1 prop.3 X -rm $(BINDIR)/getprop $(BINDIR)/putprop $(BINDIR)/remprop X cp prop $(BINDIR)/getprop X ln $(BINDIR)/getprop $(BINDIR)/putprop X ln $(BINDIR)/getprop $(BINDIR)/remprop X cp prop.1 prop.3 $(MANDIR) X Xclean: X -rm $(ALLTARGETS) $(PROPOBJS) getprop putprop remprop END_OF_FILE if test 1193 -ne `wc -c <'Makefile'`; then echo shar: \"'Makefile'\" unpacked with wrong size! fi # end of 'Makefile' fi if test -f 'prop.1.nr' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'prop.1.nr'\" else echo shar: Extracting \"'prop.1.nr'\" \(2291 characters\) sed "s/^X//" >'prop.1.nr' <<'END_OF_FILE' X.TH PROP 1 X.SH "NAME" Xprop \- Manipulate property-list-style file headers X.SH "SYNOPSIS" X.B getprop X[-rms] [-a] [-Tx] Xfile property X.br X.B putprop X[-rms] [-Tx] Xfile property value X.br X.B remprop X[-rms] [-Tx] Xfile property value X.SH "DESCRIPTION" XThe X.B prop Xsuite of programs manipulate the header of a file. The header is defined Xsimilarly to a mail header in X.I RFC822: Xa block of name-value pairs Xfollowed by a blank line. XThe remainder of the file can be anything, and is maintained intact by the X.B prop Xprograms. X.PP XThe format of the name-value pairs is fairly flexible. By default, these Xprograms will recognise either a colon or an equal-sign to terminate the Xname. The value may be quoted using X.I RFC-822, Makefile, Xor X.I /bin/sh Xconventions. X.PP X.I RFC-822 Xproperties are of the form \fB"Name: value"\fR, with continuation Xlines indented. X.I Makefile Xproperties are of the form \fB"NAME= value"\fR, with Xcontinued lines escaped by a backslash. \fI/bin/sh\fR properties are of the Xform \fB"name='value'"\fR, with newlines embedded in the quoted string. X.SH "COMMANDS" X.B putprop Xis used to create a proplist file, and add name-value pairs to it. If the Xname already exists, putprop will override it. X.PP X.B getprop Xwill return one or more properties from the file. X.PP X.B remprop Xwill delete properties from the file. X.SH "OPTIONS" X.IP \fB-r\fR XUse \fIRFC822\fR style properties (default). X.IP \fB-s\fR XUse \fI/bin/sh\fR style properties. X.IP \fB-m\fR XUse \fIMakefile\fR style properties. X.IP \fB-a\fR X(getprop only) XDisplay all properties for this name. \fBputprop\fR will not create such Xa condition, but they are possible in \fIRFC822\fR... for example, the X\fB"Received:"\fR header entry. X.IP \fB-T\fIc\fR XSet the tag character to \fIc\fR. XBy default, this is either \fB=\fR or \fB:\fR, depending Xon which is first recognised in the file. X.SH "EXAMPLES" XIn an automatically configured makefile, you could update the Xconfiguration with \fBputprop -m Makefile SHELL /bin/sh.\fR X.PP XTo update a shell script, Xtry \fBputprop -s ~/.profile ORGANIZATION "$ORGANIZATION"\fR. X.SH "SEE ALSO" X.B prop (3) X.SH "AUTHOR" X.IP "Peter da Silva" 2.0i Xoriginal, Hackercorp X.SH SUPPORT XThis software is X.I not Xsupported. X.PP XSend comments or improvements to \fBpeter@sugar.hackercorp.com\fR END_OF_FILE if test 2291 -ne `wc -c <'prop.1.nr'`; then echo shar: \"'prop.1.nr'\" unpacked with wrong size! fi # end of 'prop.1.nr' fi if test -f 'prop.3.nr' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'prop.3.nr'\" else echo shar: Extracting \"'prop.3.nr'\" \(2387 characters\) sed "s/^X//" >'prop.3.nr' <<'END_OF_FILE' X.\" to produce the man page. X.\" troff -man -Tdumb cpr.tf | ddumb >cpr.p X.\" X.TH PROP 3 X.SH "NAME" Xprop \- Manipulate property-list-style file headers X.SH "SYNOPSIS" X#include "prop.h" X X.B getprop( Xfile, property, buffer X.B ); X.br X.B putprop( Xfile, property, value X.B ); X.br X.B remprop( Xfile, property X.B ); X.SH "DESCRIPTION" XThe X.B prop Xsuite of routines manipulate the header of a file. The header is defined Xsimilarly to a mail header in \fIRFC822\fR: a block of name-value pairs Xfollowed by a blank line. XThe remainder of the file can be anything, and is maintained intact by the X.B prop Xroutines. X.PP XThe format of the name-value pairs is fairly flexible. By default, these Xprograms will recognise either a colon or an equal-sign to terminate the Xname. The value may be quoted using \fIRFC-822, Makefile\fR, or \fI/bin/sh\fR Xconventions. X.PP X.I RFC-822 Xproperties are of the form \fB"Name: value"\fR, with continuation Xlines indented. X.I Makefile Xproperties are of the form \fB"NAME= value"\fR, with Xcontinued lines escaped by a backslash. X.I /bin/sh Xproperties are of the Xform \fB"name='value'"\fR, with newlines embedded in the quoted string. X.SH "ROUTINES" X.B putprop Xis used to create a proplist file, and add name-value pairs to it. If any Xname already exists, \fBputprop\fR will override it. X.PP X.B getprop Xwill return one or more properties from the file. X.PP X.B remprop Xwill delete properties from the file. X.SH "EXTERNAL VARIABLES" X.IP \fBFlags\fR XA bit-map containing various flags. Currently only X.B ALLFLAG Xis defined. If X.B (Flags&ALLFLAG) Xis true, then X.B getprop Xwill put all properties with the right name in the Xbuffer. X.IP \fBStyle\fR XA byte containing the style to use for the proplist: \fBRFC\fR for X.I RFC822 Xstyle, X.B BINSH Xfor X.I /bin/sh Xstyle, or X.B MAKEFILE Xfor X.I Makefile Xstyle. X.IP \fBDefaultTag\fR XA byte containing the character to use to flag a variable, used by X.B putprop Xfor creating a proplist the first time. Initialised to \fB':'\fR, and should be Xchanged to \fB'='\fR if you use X.B BINSH Xor X.B MAKEFILE Xmode. X.IP \fBTagChar\fR XA byte containing the character to use to flag a variable, used by all Xroutines. This should normally be X.I NULL. X.SH "SEE ALSO" X.B prop (1) X.SH "AUTHOR" X.IP "Peter da Silva" 2.0i Xoriginal, Hackercorp X.SH SUPPORT XThis software is X.I not Xsupported. X.PP XSend comments or improvements to X.B peter@sugar.hackercorp.com END_OF_FILE if test 2387 -ne `wc -c <'prop.3.nr'`; then echo shar: \"'prop.3.nr'\" unpacked with wrong size! fi # end of 'prop.3.nr' fi if test -f 'prop.h' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'prop.h'\" else echo shar: Extracting \"'prop.h'\" \(254 characters\) sed "s/^X//" >'prop.h' <<'END_OF_FILE' X X#define SUCCESS 0 X#define FAILURE 1 X#define ERROR 2 X X#define NOFLAG 0 X#define ALLFLAG (1<<0) X X#define MAKEFILE 'm' X#define RFC 'r' X#define BINSH 's' X Xextern char TagChar; Xextern char DefaultTag; X Xextern unsigned long Flags; X Xextern unsigned char Style; END_OF_FILE if test 254 -ne `wc -c <'prop.h'`; then echo shar: \"'prop.h'\" unpacked with wrong size! fi # end of 'prop.h' fi if test -f 'main.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'main.c'\" else echo shar: Extracting \"'main.c'\" \(2059 characters\) sed "s/^X//" >'main.c' <<'END_OF_FILE' X/* Program for manipulating property lists, maintained as headers for a file */ X X/* Usage: X * getprop [options] file property X * remprop [options] file property X * putprop [options] file property value X */ X X#include X X#include "prop.h" X Xchar *name; X Xusage() X{ X fprintf(stderr, "Usage:\n"); X fprintf(stderr, "\tgetprop [options] file property\n"); X fprintf(stderr, "\tremprop [options] file property\n"); X fprintf(stderr, "\tputprop [options] file property value\n"); X fprintf(stderr, "Options:\n"); X fprintf(stderr, "\t-Tc\tTag character is 'c' (default ':').\n"); X fprintf(stderr, "\t-a\tExtract all matching entries.\n"); X fprintf(stderr, "\t-r\tRFC style (default) (sets default tag to ':').\n"); X fprintf(stderr, "\t-m\tMAKE style (sets default tag to '=').\n"); X fprintf(stderr, "\t-s\t/bin/sh style (sets default tag to '=').\n"); X} X Xargs(ac, av, n) Xint ac; Xchar **av; Xint n; X{ X if(ac!=n) X { X fprintf(stderr, "%s: Argument count.\n", name); X usage(); X exit(ERROR); X } X} X Xmain(ac, av) Xint ac; Xchar **av; X{ X char *strrchr(); X int flag; X char buffer[BUFSIZ]; X X if(name = strrchr(av[0], '/')) X name++; X else X name = av[0]; X X while(av[1][0] == '-') { X av++; X ac--; X while(*++*av) { X switch(**av) { X case 'T': X TagChar = *++*av; X if(!TagChar) { X fprintf(stderr, "%s: Missing tag character\n", name); X usage(); X exit(2); X } X break; X case 'a': X Flags |= ALLFLAG; X break; X case 'm': X DefaultTag = '='; X Style = MAKEFILE; X break; X case 'r': X DefaultTag = ':'; X Style = RFC; X break; X case 's': X DefaultTag = '='; X Style = BINSH; X break; X default: X fprintf(stderr, "%s: Unknown option -%c\n", name, **av); X usage(); X exit(2); X } X } X } X X switch(name[0]) X { X default: X case 'g': X args(ac, av, 3); X if(flag = getprop(av[1], av[2], buffer) == SUCCESS) X puts(buffer); X return flag; X case 'r': X args(ac, av, 3); X return remprop(av[1], av[2]); X case 'p': X args(ac, av, 4); X return putprop(av[1], av[2], av[3]); X } X} END_OF_FILE if test 2059 -ne `wc -c <'main.c'`; then echo shar: \"'main.c'\" unpacked with wrong size! fi # end of 'main.c' fi if test -f 'prop.c' -a "${1}" != "-c" ; then echo shar: Will not clobber existing file \"'prop.c'\" else echo shar: Extracting \"'prop.c'\" \(9162 characters\) sed "s/^X//" >'prop.c' <<'END_OF_FILE' X/* Routines for manipulating a "property list" file: X * X *------ X * name-value-pair X * name-value-pair X * X * Anything after the blank line is ignored by getprop, maintained by X * putprop/remprop. X *------ X * The name/value pairs can be in various formats: X * X * RFC: This is in RFC format (default). Space after the colon, and X * property continued on sucessive lines by indenting it. X * X * Makefile: This is in Makfile format. This is similar to RFC format,\ X * but continued lines have a backslash at the end. X * X * Shell='This is in shell format: no space after equal sign, property X * in single quotes on multiple lines. Quotes inside the line are escaped X * like so: \'. Will accept "..." quotes and mixed quotes, but will turn X * it back into the standard form if the file is modified.' X * X * For future work: a c-shell format: X * X * set Cshell='This is what the c-shell format would be like: the keyword \ X * "set" would be required, and lines would be continued with a space and a \ X * backslash. I don't want to bother with this one.' X */ X#include X#include X X#include "prop.h" X Xchar TagChar = NULL; /* What character we're using to tag variables */ Xchar DefaultTag = ':'; /* what character to use for a new proplist file */ Xunsigned long Flags = NOFLAG; /* various flags affecting our behaviour */ Xunsigned char Style = RFC; /* Style of variables */ X X#ifdef RENAME Xstatic rename(from, to) Xchar *from, *to; X{ X if(link(from, to) == 0) X unlink(from); X} X#endif X X/* Open a file and read a particular property out of it */ X X/* Arguments: name of file, name of property, and buffer to return value */ X X/* Returns: ERROR if file doesn't exist or is malformed, FAILURE if the X * property is not found. SUCCESS if the property is found. X */ Xgetprop(file, prop, val) Xchar *file; Xchar *prop; Xchar val[BUFSIZ]; X{ X FILE *fp; X char *propname, *propval; X char buffer[BUFSIZ]; X int state; X X state = ERROR; X X if(!(fp = fopen(file, "r"))) { X perror(file); X return state; X } X X state = FAILURE; X X while(getline(fp, buffer, &propname, &propval)) { X if(match(prop, propname)) { X if(state != SUCCESS) { X state = SUCCESS; X val[0] = 0; X } else { X if(strlen(val) + strlen(propval) + 1 > BUFSIZ) { X state = ERROR; X break; X } X strcat(val, "\n"); X } X strcat(val, propval); X if( (Flags&ALLFLAG) == NOFLAG ) X break; X } X } X fclose(fp); X return state; X} X X/* Open a file and insert a given property in it. */ X X/* Arguments: name of file, name of property, and value */ X X/* Results: ERROR if file can't be opened or other problem occurs. X * SUCCESS if property sucessfully saved. X */ Xputprop(file, prop, val) Xchar *file; Xchar *prop; Xchar *val; X{ X FILE *fp, *tfp; X char tempfile[BUFSIZ]; X char buffer[BUFSIZ], *propname, *propval; X X fp = fopen(file, "r"); X X buildtemp(tempfile, file); X X if(!(tfp = fopen(tempfile, "w"))) { X perror(tempfile); X if(fp) fclose(fp); X return ERROR; X } X X if(fp) { X while(getline(fp, buffer, &propname, &propval)) { X if(!match(prop, propname)) { X if(!putline(tfp, propname, propval)) { X fclose(fp); X fclose(tfp); X perror(tempfile); X unlink(tempfile); X return ERROR; X } X } else { X if(!putline(tfp, prop, val)) { X if(fp) fclose(fp); X fclose(tfp); X perror(tempfile); X unlink(tempfile); X return ERROR; X } X *prop = 0; X } X } X } X X if(*prop && !putline(tfp, prop, val)) { X if(fp) fclose(fp); X fclose(tfp); X perror(tempfile); X unlink(tempfile); X return ERROR; X } X X if(fp) { X if(!copyfile(fp, tfp)) { X fclose(fp); X fclose(tfp); X perror(tempfile); X unlink(tempfile); X return ERROR; X } X } X if(fp) fclose(fp); X fclose(tfp); X if(fp) unlink(file); X rename(tempfile, file); X return SUCCESS; X} X X/* Removes an entry from a file. */ X X/* Arguments: file name and property. */ X X/* Returns: SUCCESS or ERROR. It sould probably be rewritten to avoid X * rewriting the file if the property isn't found. X */ Xremprop(file, prop) Xchar *file; Xchar *prop; X{ X FILE *fp, *tfp; X char tempfile[BUFSIZ]; X char buffer[BUFSIZ], *propname, *propval; X X if(!(fp = fopen(file, "r"))) { X perror(file); X return ERROR; X } X X buildtemp(tempfile, file); X X if(!(tfp = fopen(tempfile, "w"))) { X perror(tempfile); X fclose(fp); X return ERROR; X } X X while(getline(fp, buffer, &propname, &propval)) X if(!match(prop, propname)) X if(!putline(tfp, propname, propval)) { X fclose(fp); X fclose(tfp); X perror(tempfile); X unlink(tempfile); X return ERROR; X } X X if(!copyfile(fp, tfp)) { X fclose(fp); X fclose(tfp); X perror(tempfile); X unlink(tempfile); X return ERROR; X } X fclose(fp); X fclose(tfp); X unlink(file); X rename(tempfile, file); X return SUCCESS; X} X X/* Read a line in RFC format, with possible continuations */ Xrfcreadline(fp, buffer) XFILE *fp; Xchar buffer[BUFSIZ]; X{ X int c; X char *p; X X p = buffer; X while((c = getc(fp)) != EOF) { X if(c=='\n') { X c = getc(fp); X if(c==EOF) break; X if(c!='\t' && c!=' ') { X ungetc(c, fp); X break; X } X c = '\n'; X } X if(p - buffer < BUFSIZ-1) X *p++ = c; X } X *p = 0; X} X X/* Read a line in Makefile format, with possible continuations */ Xmakereadline(fp, buffer) XFILE *fp; Xchar buffer[BUFSIZ]; X{ X int c; X char *p; X X p = buffer; X while((c = getc(fp)) != EOF) { X if(c == '\\') { X c = getc(fp); X if(c==EOF) break; X if(c!='\n') { X ungetc(c, fp); X c = '\\'; X } X } else if(c=='\n') { X break; X } X if(p - buffer < BUFSIZ-1) X *p++ = c; X } X *p = 0; X} X X/* read a line in /bin/sh format, with possible continuations */ X X/* Tries to interpret quotes, backslash, semicolons, and comments. X * Semicolons will be replaced by linefeeds on output. Comments will X * be eaten. X */ X X/* Backslash handling is rather simple: backslash is kept before X * quotes to simplify regeneration of the string. Other escapes X * are simply replaced by the character, so will be hidden by the X * quoted string when the line is re-output. X */ Xshreadline(fp, buffer) XFILE *fp; Xchar buffer[BUFSIZ]; X{ X int c; X char *p; X X p = buffer; X while((c = getc(fp)) != EOF) { X if(c=='\\') { /* handle backslash */ X c = getc(fp); X if(c!='\'') X if(p - buffer < BUFSIZ-1) X *p++ = '\\'; X if(c==EOF) break; X if(p - buffer < BUFSIZ-1) X *p++ = c; X } else if(c=='"' || c=='\'') { X char quote = c; X while((c = getc(fp)) != EOF) { X if(c=='\\') { X c = getc(fp); X if(p - buffer < BUFSIZ-1) X *p++ = '\\'; X if(c==EOF) break; X if(p - buffer < BUFSIZ-1) X *p++ = c; X } else { X if(c==quote) X break; X if(p - buffer < BUFSIZ-1) X *p++ = c; X } X } X if(c==EOF) break; X } else if(strchr(" \t\n;#", c)) { X break; X } else { X if(p - buffer < BUFSIZ-1) X *p++ = c; X } X } X *p = 0; X} X X/* Read a line... see what the format is and call the correct routine. Then X * convert the line into name-value pair. X */ Xgetline(fp, buffer, nameptr, valptr) XFILE *fp; Xchar buffer[BUFSIZ]; Xchar **nameptr; Xchar **valptr; X{ X int c; X char *p; X X if(feof(fp)) X return 0; X X if(Style == RFC) X rfcreadline(fp, buffer); X else if(Style == MAKEFILE) X makereadline(fp, buffer); X else if(Style == BINSH) X shreadline(fp, buffer); X X p = buffer; X X while(isspace(*p)) X p++; X X if(!*p) X return 0; X X *nameptr = p; X while(*p && *p != TagChar && *p != ':' && *p != '=') { X p++; X } X X if(*p) { X if(TagChar == NULL) X TagChar = *p; X *p++ = 0; X while(isspace(*p)) X p++; X } X X *valptr = p; X} X X/* Disctionary comparison of two strings */ Xmatch(s1, s2) Xchar *s1, *s2; X{ X register char c1, c2; X X while(*s1 && *s2) { X c1 = *s1++; X c2 = *s2++; X X if(isupper(c1)) X c1 = tolower(c1); X if(isupper(c2)) X c2 = tolower(c2); X X if(c1 != c2) X return 0; X } X X return !(*s1 || *s2); X} X X/* Putline: puts the name, the tag, and the value. Does various aesthetic X * stuff and quotes continuations and so on. X */ Xputline(fp, name, val) XFILE *fp; Xchar *name, *val; X{ X while(*name) { X putc(*name, fp); X name++; X } X if(*val || Style == BINSH) { X if(!TagChar) X TagChar = DefaultTag; X putc(TagChar, fp); X } X if(*val) { X if(Style == RFC || Style == MAKEFILE) { /* EZ, just continue */ X putc(' ', fp); X while(*val) { X if(*val == '\n' && Style == MAKEFILE) X putc('\\', fp); X putc(*val, fp); X if(*val=='\n' && Style == RFC) X putc('\t', fp); X val++; X } X } else { /* More complex, have to quote ' and \ as well */ X putc('\'', fp); X while(*val) { X if(*val=='\\') { X putc(*val, fp); X val++; X if(!*val) break; X } else if(*val=='\'') { X putc('\\', fp); X } X putc(*val, fp); X val++; X } X putc('\'', fp); X } X } X putc('\n', fp); X} X X/* Copy everything from one stream to another. Used to copy rest of file X * after the header. X */ Xcopyfile(fp1, fp2) XFILE *fp1, *fp2; X{ X char buffer[BUFSIZ]; X if(feof(fp1)) X return; X X putc('\n', fp2); X while(fgets(buffer, BUFSIZ, fp1)) X if(!fputs(buffer, fp2)) X return 0; X X return 1; X} X X/* Creates a temp file in the same directory (thus same file system) as X * the original. X */ Xbuildtemp(temp, file) Xchar temp[BUFSIZ]; Xchar *file; X{ X char *p, *s, *strrchr(); X X s = strrchr(file, '/'); X X p = temp; X X if(s) X s++; X else X *p++ = '@'; X X while(*file) { X if(file==s) X *p++ = '@'; X *p++ = *file++; X } X X *p++ = 0; X} END_OF_FILE if test 9162 -ne `wc -c <'prop.c'`; then echo shar: \"'prop.c'\" unpacked with wrong size! fi # end of 'prop.c' fi echo shar: End of shell archive. exit 0 -- _--_|\ Peter da Silva. +1 713 274 5180. . / \ \_.--._/ Xenix Support -- it's not just a job, it's an adventure! v "Have you hugged your wolf today?" `-_-'