text: #include <u.h>
  #include <libc.h>
  #include <bio.h>
  #include <libsec.h>
  #include <ctype.h>
  
  #include "iso9660.h"
  
  /*
   * ISO 9660 file names must be uppercase, digits, or underscore.
   * We use lowercase, digits, and underscore, translating lower to upper
   * in mkisostring, and upper to lower in isostring.
   * Files with uppercase letters in their names are thus nonconforming.
   * Conforming files also must have a basename
   * at most 8 letters and at most one suffix of at most 3 letters.
   */
  char*
  isostring(uchar *buf, int len)
  {
  	char *p, *q;
  
  	p = emalloc(len+1);
  	memmove(p, buf, len);
  	p[len] = '\0';
  	while(len > 0 && p[len-1] == ' ')
  		p[--len] = '\0';
  	for(q=p; *q; q++)
  		*q = tolower(*q);
  
  	q = atom(p);
  	free(p);
  	return q;
  }
  
  int 
  isisofrog(char c)
  {
  	if(c >= '0' && c <= '9')
  		return 0;
  	if(c >= 'a' && c <= 'z')
  		return 0;
  	if(c == '_')
  		return 0;
  
  	return 1;
  }
  
  int
  isbadiso9660(char *s)
  {
  	char *p, *q;
  	int i;
  
  	if((p = strchr(s, '.')) != nil) {
  		if(p-s > 8)
  			return 1;
  		for(q=s; q<p; q++)
  			if(isisofrog(*q))
  				return 1;
  		if(strlen(p+1) > 3)
  			return 1;
  		for(q=p+1; *q; q++)
  			if(isisofrog(*q))
  				return 1;
  	} else {
  		if(strlen(s) > 8)
  			return 1;
  		for(q=s; *q; q++)
  			if(isisofrog(*q))
  				return 1;
  
  		/*
  		 * we rename files of the form [FD]dddddd
  		 * so they don't interfere with us.
  		 */
  		if(strlen(s) == 7 && (s[0] == 'D' || s[0] == 'F')) {
  			for(i=1; i<7; i++)
  				if(s[i] < '0' || s[i] > '9')
  					break;
  			if(i == 7)
  				return 1;
  		}
  	}
  	return 0;
  }
  
  /*
   * ISO9660 name comparison
   * 
   * The standard algorithm is as follows:
   *   Take the filenames without extensions, pad the shorter with 0x20s (spaces),
   *   and do strcmp.  If they are equal, go on.
   *   Take the extensions, pad the shorter with 0x20s (spaces),
   *   and do strcmp.  If they are equal, go on.
   *   Compare the version numbers.
   *
   * Since Plan 9 names are not allowed to contain characters 0x00-0x1F,
   * the padded comparisons are equivalent to using strcmp directly.
   * We still need to handle the base and extension differently,
   * so that .foo sorts before !foo.foo.
   */
  int
  isocmp(const void *va, const void *vb)
  {
  	int i;
  	char s1[32], s2[32], *b1, *b2, *e1, *e2;
  	const Direc *a, *b;
  
  	a = va;
  	b = vb;
  
  	strecpy(s1, s1+sizeof s1, a->confname);
  	b1 = s1;
  	strecpy(s2, s2+sizeof s2, b->confname);
  	b2 = s2;
  	if((e1 = strchr(b1, '.')) != nil)
  		*e1++ = '\0';
  	else
  		e1 = "";
  	if((e2 = strchr(b2, '.')) != nil)
  		*e2++ = '\0';
  	else
  		e2 = "";
  
  	if((i = strcmp(b1, b2)) != 0)
  		return i;
  
  	return strcmp(e1, e2);
  }
  
  static char*
  mkisostring(char *isobuf, int n, char *s)
  {
  	char *p, *q, *eq;
  
  	eq = isobuf+n;
  	for(p=s, q=isobuf; *p && q < eq; p++)
  		if('a' <= *p && *p <= 'z')
  			*q++ = *p+'A'-'a';
  		else
  			*q++ = *p;
  
  	while(q < eq)
  		*q++ = ' ';
  
  	return isobuf;
  }
  
  void
  Cputisopvd(Cdimg *cd, Cdinfo info)
  {
  	char buf[130];
  
  	Cputc(cd, 1);				/* primary volume descriptor */
  	Cputs(cd, "CD001", 5);			/* standard identifier */
  	Cputc(cd, 1);				/* volume descriptor version */
  	Cputc(cd, 0);				/* unused */
  
  	assert(~info.flags & (CDplan9|CDrockridge));
  
  	/* system identifier */
  	strcpy(buf, "");
  	if(info.flags & CDplan9)
  		strcat(buf, "plan 9 ");
  	if(info.flags & CDrockridge)
  		strcat(buf, "rrip ");
  	if(info.flags & CDbootable)
  		strcat(buf, "boot ");
  	if(info.flags & CDconform)
  		strcat(buf, "iso9660");
  	else
  		strcat(buf, "utf8");
  	
  	struprcpy(buf, buf);
  	Cputs(cd, buf, 32);
  
  	Cputs(cd, mkisostring(buf, 32, info.volumename), 32);			/* volume identifier */
  
  	Crepeat(cd, 0, 8);				/* unused */
  	Cputn(cd, 0, 4);				/* volume space size */
  	Crepeat(cd, 0, 32);				/* unused */
  	Cputn(cd, 1, 2);				/* volume set size */
  	Cputn(cd, 1, 2);				/* volume sequence number */
  	Cputn(cd, Blocksize, 2);			/* logical block size */
  	Cputn(cd, 0, 4);				/* path table size */
  	Cputnl(cd, 0, 4);				/* location of Lpath */
  	Cputnl(cd, 0, 4);				/* location of optional Lpath */
  	Cputnm(cd, 0, 4);				/* location of Mpath */
  	Cputnm(cd, 0, 4);				/* location of optional Mpath */
  	Cputisodir(cd, nil, DTroot, 1, Cwoffset(cd));			/* root directory */
  
  	Cputs(cd, mkisostring(buf, 128, info.volumeset), 128);		/* volume set identifier */
  	Cputs(cd, mkisostring(buf, 128, info.publisher), 128);			/* publisher identifier */
  	Cputs(cd, mkisostring(buf, 128, info.preparer), 128);			/* data preparer identifier */
  	Cputs(cd, mkisostring(buf, 128, info.application), 128);		/* application identifier */
  
  	Cputs(cd, "", 37);			/* copyright notice */
  	Cputs(cd, "", 37);			/* abstract */
  	Cputs(cd, "", 37);			/* bibliographic file */
  	Cputdate1(cd, now);				/* volume creation date */
  	Cputdate1(cd, now);				/* volume modification date */
  	Cputdate1(cd, 0);				/* volume expiration date */
  	Cputdate1(cd, 0);				/* volume effective date */
  	Cputc(cd, 1);				/* file structure version */
  	Cpadblock(cd);
  }
  