/* pptfs.c © Steve Simon 2007 */
#include <u.h>
#include <libc.h>
#include <bio.h>
#include <ctype.h>
#include <auth.h>
#include <fcall.h>
#include <thread.h>
#include <9p.h>
enum {
PPTMAGIC = 0xe391c05f,
Isdir = 0x0f
};
typedef struct {
vlong base; // start of record in file
int cont; // is 0x0f if its a container
int opts; // record options
int type; // record type
int len; // length of current record
int got; // remaining record data
} Hdr;
typedef struct {
int num; // slide number ?
vlong off; // file offset
} Blk;
typedef struct {
int bullet; // true if lines have bullets
char allign; // l=left, r=right, c=center
} Para;
typedef struct {
char *type;
int len;
int off;
} Img;
typedef struct {
int size;
void *table;
int alloc;
int used;
} Tab;
typedef struct {
int slide;
int blip;
} Xref;
Tab Imgtab = { sizeof(Img) }; // Blip Images (in order found)
Tab Blktab = { sizeof(Blk) }; // edited block of slides (sorted by slide ID)
Tab Xreftab = { sizeof(Xref) }; // maps blips to slides
int Slideno = -1; // current slide number
int Margin = 1; // am at the margin
int Txtype = -1; // type of current text (title, body etc)
vlong Start; // first edited slide chunk
Fmt *Fp;
int Debug = 0;
Biobuf *Bi = nil;
int parse(vlong off, int len, int indent);
typedef struct {
char *data;
int len;
} Vfile;
struct {
char *s;
int n;
} Types[] = {
{ "Unknown", 0 },
{ "Document", 1000 },
{ "Document Atom", 1001 },
{ "End Document", 1002 },
{ "Slide", 1006 },
{ "Slide Atom", 1007 },
{ "Notes", 1008 },
{ "Notes Atom", 1009 },
{ "Environment", 1010 },
{ "Slide Persist Atom", 1011 },
{ "S Slide Layout Atom", 1015 },
{ "Main Master", 1016 },
{ "SS Slide Info Atom", 1017 },
{ "Slide View Info", 1018 },
{ "Guide Atom", 1019 },
{ "View Info", 1020 },
{ "View Info Atom", 1021 },
{ "Slide View Info Atom", 1022 },
{ "VBA Info", 1023 },
{ "VBA Info Atom", 1024 },
{ "SS Doc Info Atom", 1025 },
{ "Summary", 1026 },
{ "Doc Routing Slip", 1030 },
{ "Outline View Info", 1031 },
{ "Sorter View Info", 1032 },
{ "Ex Obj List", 1033 },
{ "Ex Obj List Atom", 1034 },
{ "PP Drawing Group", 1035 },
{ "PP Drawing", 1036 }, // Escher container
{ "Named Shows", 1040 },
{ "Named Show", 1041 },
{ "Named Show Slides", 1042 },
{ "List", 2000 },
{ "Font Collection", 2005 },
{ "Bookmark Collection", 2019 },
{ "Sound Coll Atom", 2021 },
{ "Sound", 2022 },
{ "Sound Data", 2023 },
{ "Bookmark Seed Atom", 2025 },
{ "Color Scheme Atom", 2032 },
{ "Ex Obj Xref Atom", 3009 },
{ "OE Shape Atom", 3009 },
{ "OE Placeholder Atom", 3011 },
{ "G Point Atom", 3024 },
{ "G Ratio Atom", 3031 },
{ "Outline Text Xref Atom", 3998 },
{ "Text Header Atom", 3999 },
{ "Text Chars Atom", 4000 },
{ "Style Text Prop Atom", 4001 },
{ "Base Text Prop Atom", 4002 },
{ "Tx Master Style Atom", 4003 },
{ "Tx CF Style Atom", 4004 },
{ "Tx PF Style Atom", 4005 },
{ "Text Ruler Atom", 4006 },
{ "Text Bookmark Atom", 4007 },
{ "Text Bytes Atom", 4008 },
{ "Tx SI Style Atom", 4009 },
{ "Text Spec Info Atom", 4010 },
{ "Default Ruler Atom", 4011 },
{ "Font Entity Atom", 4023 },
{ "Font Embedded Data", 4024 },
{ "C String", 4026 },
{ "Meta File", 4033 },
{ "Ex Ole Obj Atom", 4035 },
{ "Sr Kinsoku", 4040 },
{ "Hand Out", 4041 },
{ "Ex Embed", 4044 },
{ "Ex Embed Atom", 4045 },
{ "Ex Link", 4046 },
{ "Bookmark Entity Atom", 4048 },
{ "Ex Link Atom", 4049 },
{ "Sr Kinsoku Atom", 4050 },
{ "Ex Hyperlink Atom", 4051 },
{ "Ex Hyperlink", 4055 },
{ "Slide Number MC Atom", 4056 },
{ "Headers Footers", 4057 },
{ "Headers Footers Atom", 4058 },
{ "Tx Interactive Info Atom", 4063 },
{ "Char Format Atom", 4066 },
{ "Para Format Atom", 4067 },
{ "Recolor Info Atom", 4071 },
{ "Ex Quick Time Movie", 4074 },
{ "Ex Quick Time Movie Data", 4075 },
{ "Ex Control", 4078 },
{ "Slide List With Text", 4080 },
{ "Interactive Info", 4082 },
{ "Interactive Info Atom", 4083 },
{ "User Edit Atom", 4085 },
{ "Current User Atom", 4086 },
{ "Date Time MC Atom", 4087 },
{ "Generic Date MC Atom", 4088 },
{ "Footer MC Atom", 4090 },
{ "Ex Control Atom", 4091 },
{ "Ex Media Atom", 4100 },
{ "Ex Video", 4101 },
{ "Ex Avi Movie", 4102 },
{ "Ex MCI Movie", 4103 },
{ "Ex MIDI Audio", 4109 },
{ "Ex CD Audio", 4110 },
{ "Ex WAV Audio Embedded", 4111 },
{ "Ex WAV Audio Link", 4112 },
{ "Ex Ole Obj Stg", 4113 },
{ "Ex CD Audio Atom", 4114 },
{ "Ex WAV Audio Embedded Atom", 4115 },
{ "Animation Info Atom", 4116 },
{ "RTF Date Time MC Atom", 4117 },
{ "Prog Tags", 5000 },
{ "Prog String Tag", 5001 },
{ "Prog Binary Tag", 5002 },
{ "Binary Tag Data", 5003 },
{ "Print Options", 6000 },
{ "Persist Ptr Full Block", 6001 },
{ "Persist Ptr Inc Block", 6002 },
{ "G Scaling Atom", 10001 },
{ "GR Color Atom", 10002 },
{ "Escher Dgg Container", 0xf000 },
{ "Escher Dgg", 0xf006 },
{ "Escher CLSID", 0xf016 },
{ "Escher OPT", 0xf00b },
{ "Escher BStore Container", 0xf001 },
{ "Escher BSE", 0xf007 },
{ "Escher Blip first", 0xf018 },
{ "Escher Blip unknown", 0xf019 },
{ "Escher Blip EMF", 0xf01a },
{ "Escher Blip WMF", 0xf01b },
{ "Escher Blip PICT", 0xf01c },
{ "Escher Blip JPEG", 0xf01d },
{ "Escher Blip PNG", 0xf01e },
{ "Escher Blip DIB", 0xf01f },
{ "Escher Blip last", 0xf117 },
{ "Escher Dg Container", 0xf002 },
{ "Escher Dg", 0xf008 },
{ "Escher Regroup Items", 0xf118 },
{ "Escher Color Scheme", 0xf120 }, /* bug in docs */
{ "Escher Spgr Container", 0xf003 },
{ "Escher Sp Container", 0xf004 },
{ "Escher Spgr", 0xf009 },
{ "Escher Sp", 0xf00a },
{ "Escher Textbox", 0xf00c },
{ "Escher Client Textbox", 0xf00d },
{ "Escher Anchor", 0xf00e },
{ "Escher Child Anchor", 0xf00f },
{ "Escher Client Anchor", 0xf010 },
{ "Escher Client Data", 0xf011 },
{ "Escher Solver Container", 0xf005 },
{ "Escher Connector Rule", 0xf012 } , /* bug in docs */
{ "Escher Align Rule", 0xf013 },
{ "Escher Arc Rule", 0xf014 },
{ "Escher Client Rule", 0xf015 },
{ "Escher Callout Rule", 0xf017 },
{ "Escher Selection", 0xf119 },
{ "Escher Color MRU", 0xf11a },
{ "Escher Deleted Pspl", 0xf11d } , /* bug in docs */
{ "Escher Split Menu Colors", 0xf11e },
{ "Escher Ole Object", 0xf11f },
{ "Escher User Defined", 0xf122 },
};
void fsread(Req *);
Srv Fs = {
.read = fsread,
};
void
pr(char *fmt, ...)
{
va_list args;
va_start(args, fmt);
if (Debug)
vfprint(1, fmt, args);
fmtvprint(Fp, fmt, args);
va_end(args);
}
void *
growtab(Tab *t)
{
if(t->alloc >= t->used){
t->alloc += 8;
t->table = realloc(t->table, t->alloc * t->size);
if(t->table == nil)
sysfatal("no memory - %r");
}
return (char *)t->table + t->used++ * t->size;
}
void *
indextab(Tab *t, int idx)
{
if (idx < 0 || idx >= t->used)
return nil;
return (char *)t->table + idx * t->size;
}
int
mkfile(char *name, char *data, int len)
{
int seq;
char *p;
Vfile *vf;
File *f, *nf;
char s[128];
vf = emalloc9p(sizeof(Vfile));
vf->data = data;
vf->len = len;
f = Fs.tree->root;
incref(f);
while(f && (p = strchr(name, '/'))) {
*p = '\0';
if(strcmp(name, "") != 0 && strcmp(name, ".") != 0){
/* this would be a race if we were multithreaded */
incref(f); /* so walk doesn't kill it immediately on failure */
if((nf = walkfile(f, name)) == nil)
nf = createfile(f, name, "bill", DMDIR|0755, nil);
decref(f);
f = nf;
}
*p = '/';
name = p+1;
}
if(f == nil){
free(vf->data);
free(vf);
return -1;
}
incref(f);
seq = 0;
snprint(s, sizeof(s), "%s", name);
while((nf = walkfile(f, s)) != nil){
f = walkfile(nf, "..");
snprint(s, sizeof(s), "%s.%d", name, seq++);
}
nf = createfile(f, s, "bill", 0644, vf);
decref(f);
nf->length = len;
return 0;
}
char *
rectype(int type)
{
int i;
static char buf[32];
for (i = 0; i < nelem(Types); i++)
if(type == Types[i].n)
return Types[i].s;
snprint(buf, nelem(buf), "0x%x", type);
return buf;
}
void
sheet(int num, vlong off)
{
int i;
Blk *bk;
for(i = 0; (bk = indextab(&Blktab, i)); i++)
if(bk->num == num)
return;
bk = growtab(&Blktab);
bk->num = num;
bk->off = off;
}
int
cmp(void *a, void *b)
{
return ((Blk *)a)->num - ((Blk *)b)->num;
}
void
xd(Hdr *h)
{
vlong off;
uchar buf[16];
int addr, got, n, i, j;
addr = 0;
off = Boffset(Bi);
while (addr < h->len){
n = (h->len >= sizeof(buf))? sizeof(buf): h->len;
got = Bread(Bi, buf, n);
print(" %6d ", addr);
addr += n;
for (i = 0; i < got; i++)
print("%02x ", buf[i]);
for (j = i; j < 16; j++)
print(" ");
print(" ");
for (i = 0; i < got; i++)
print("%c", isprint(buf[i])? buf[i]: '.');
print("\n");
}
Bseek(Bi, off, 0);
}
vlong
gint(Hdr *h, int n)
{
int i, c;
uvlong vl, rc;
assert(n <= h->got);
rc = 0;
for (i = 0; i < n; i++){
if ((c = Bgetc(Bi)) == -1)
sysfatal("unexpected EOF - %r\n");
h->got--;
vl = c;
rc |= vl << (8*i);
}
return rc;
}
static int
getrec(Hdr *h, int indent)
{
int c;
h->base = Boffset(Bi);
if ((c = Bgetc(Bi)) == -1)
return -1; // real EOF
h->cont = c & 0x0f;
h->opts = (c >> 4) & 0x0f;
if ((c = Bgetc(Bi)) == -1)
sysfatal("unexpected EOF - %r\n");
h->opts |= c << 4;
if ((c = Bgetc(Bi)) == -1)
sysfatal("unexpected EOF - %r\n");
h->type = c;
if ((c = Bgetc(Bi)) == -1)
sysfatal("unexpected EOF - %r\n");
h->type |= c << 8;
if ((c = Bgetc(Bi)) == -1)
sysfatal("unexpected EOF - %r\n");
h->got = c;
if ((c = Bgetc(Bi)) == -1)
sysfatal("unexpected EOF - %r\n");
h->got |= c << 8;
if ((c = Bgetc(Bi)) == -1)
sysfatal("unexpected EOF - %r\n");
h->got |= c << 16;
if ((c = Bgetc(Bi)) == -1)
sysfatal("unexpected EOF - %r\n");
h->got |= c << 24;
h->len = h->got +8;
if (Debug > 1){
print("%*.3s%s off=%-6lld len=%-6d %d=0x%x=%s\n",
indent, "", (h->cont == Isdir)? "cont": "atom",
h->base, h->len+8, h->type, h->type, rectype(h->type));
if (Debug > 2 && h->cont != Isdir)
xd(h);
}
return 0;
}
void
skip(Hdr *h, vlong n)
{
assert(n <= h->got);
if (Bseek(Bi, n, 1) == -1)
sysfatal("seek failed - %r\n");
h->got -= n;
}
void
putrune(Rune r, int bullet)
{
static int em = 0;
if (Margin && bullet && r != L'\r' && r != L'\n'){
pr("\t• ");
Margin = 0;
}
switch(r){
case L'\n':
case L'\r':
pr("\n");
Margin = 1;
break;
case L'':
em = 1;
break;
case L'': // FIXME: guesswork
pr(" ");
break;
case L'':
pr("``");
break;
case L'':
pr("''");
break;
case L'�':
pr("£");
break;
case L'':
if (Debug)
print("\t");
fmtprint(Fp, "\t");
break;
case L' ':
if (em)
pr("'");
else
pr(" ");
em = 0;
break;
case 0: // ignore embedded nulls
break;
default:
pr("%C", r);
break;
}
}
void
gmem(Hdr *h, void *v, int n)
{
assert(n <= h->got);
if (Bread(Bi, v, n) != n)
sysfatal("unexpected EOF - %r\n");
h->got -= n;
}
/**********************************************/
void
textcharsatom(Hdr *h, int bullet)
{
Rune r;
Margin = 1;
do {
r = Bgetrune(Bi);
h->got -= runelen(r);
putrune(r, bullet);
} while(h->got);
pr("\n");
}
void
textbytesatom(Hdr *h, int bullet)
{
int i;
Rune r;
char buf[UTFmax];
Margin = 1;
do {
i = Bgetc(Bi);
buf[0] = i;
h->got--;
chartorune(&r, buf);
putrune(r, bullet);
} while(h->got);
pr("\n");
}
void
cstring(Hdr *h, int bullet)
{
Rune r;
Margin = 1;
do {
r = Bgetrune(Bi);
h->got -= runelen(r);
putrune(r, bullet);
} while(h->got);
pr("\n");
}
void
persistincrement(Hdr *h)
{
uint x;
int i, num, off, first;
do {
x = gint(h, 4);
first = x & 0xfffff;
num = (x >> 20)&0xfff;
for(i = 0; i < num; i++){
off = gint(h, 4);
sheet(first+i, off);
}
} while(h->got);
}
void
useredit(Hdr *h, int indent)
{
vlong was;
int lastedit, persistdir;
skip(h, 4); // last slide viewed
skip(h, 4); // major ver of app
lastedit = gint(h, 4); // previous user edit
persistdir = gint(h, 4); // directory
skip(h, h->got);
was = Boffset(Bi);
if(lastedit)
parse(lastedit, 0, indent+1);
if(persistdir)
parse(persistdir, 0, indent+1);
Bseek(Bi, was, 0);
}
/*
* This record is special, it occurs only
* in the CurrentUser OLE file where it is the sole record.
* It gives detail of the offset to _start_ walking the UserEdit
* records from in the main PowerPointDocument OLE file.
*/
void
currentuser(Hdr *h)
{
int n;
char *buf;
skip(h, 4); // size of this rec
if(gint(h, 4) != PPTMAGIC)
sysfatal("bad magic number - not a powerpoint file\n");
Start = gint(h, 4);
n = gint(h, 2); // usr name len
buf = malloc(n+2);
skip(h, 4); // file version
skip(h, 1); // app major version
skip(h, 1); // app minor version
gmem(h, buf, n); // user who edited
buf[n] = '\n';
buf[n+1] = 0;
mkfile("/ppt/author", buf, n+1);
skip(h, h->got);
}
void
textheaderatom(Hdr *h, int *type)
{
*type = gint(h, 4); // text block type
if(Debug)
print("#text-type: %d\n", *type);
}
void
blipstoreentry(Hdr *h)
{
Img *i;
int m, w;
char *types[] = {
"err", "unk", "emf.gz", "wmf.gz",
"pict.gz", "jpeg", "png", "dbi"
};
i = growtab(&Imgtab);
w = gint(h, 1);
m = gint(h, 1);
if (w > 1 && w < nelem(types))
i->type = types[w];
else
if (m >= 0 && m < nelem(types))
i->type = types[m];
else
i->type = "bad";
skip(h, 16); // md4 of blip
skip(h, 2); // tag (unused)
i->len = gint(h, 4);
gint(h, 4); // ref count (unused ?)
i->off = gint(h, 4);
skip(h, h->got); // don't care about the rest
}
void
slide(Hdr *h)
{
char places[8];
int flags, geo, master, id;
geo = gint(h, 4); // geometry
gmem(h, places, sizeof(places));
master = gint(h, 4); // master of this slide of 0 if is a master
id = gint(h, 4); // id of this slide
flags = gint(h, 2); // master follow flags
if (Debug)
print("#slide: master-id=%d geo=%d flags=%x id=%d/%x\n",
master, geo, flags, id, id);
skip(h, h->got); // don't care about the rest
Slideno = id;
}
void
slidepersist(Hdr *h)
{
int ref, shapes, ntexts, id;
ref = gint(h, 4); // slide ref
shapes = gint(h, 4); // has shapes
ntexts = gint(h, 4); // num texts
id = gint(h, 4); // slide ID
if (Debug)
print("#slidepersist: ref=%d shapes=%d ntexts=%d id=%d/%x\n",
ref, shapes, ntexts, id, id);
skip(h, h->got); // don't care about the rest
Slideno = id;
}
void
notes(Hdr *h)
{
int flags, id;
id = gint(h, 4); // slide ID these notes relate to
flags = gint(h, 2); // master follow flags
if (Debug)
print("#notes: flags=%x id=%d/%x\n", flags, id, id);
skip(h, h->got); // don't care about the rest
Slideno = id;
}
void
escheropt(Hdr *h)
{
Xref *rf;
int x, blip, off, val, id;
off = 0;
blip = -1;
do{
x = gint(h, 2);
id = x & 0x3fff;
val = gint(h, 4);
if(id == 260)
blip = val;
if((x & 0x8000) != 0) // complex value
off += val;
}while(h->got >= off+6);
skip(h, h->got); // don't care about the rest
if (blip == -1) // only uninteresting info
return;
rf = growtab(&Xreftab);
rf->blip = blip;
rf->slide = Slideno;
if (Debug)
print("#escheropt: blip=%d slide=%d\n", blip-1, Slideno);
}
void
pushimage(Hdr *h, char *ext)
{
int n;
char *p, buf[64];
static int blipno = 1;
skip(h, 16); // md4 sum of image
skip(h, 1); // tag (seems always to be 0xff)
n = h->got;
p = emalloc9p(n);
gmem(h, p, n);
if (Slideno == 0)
snprint(buf, sizeof(buf), "/ppt/master/image.%s", ext);
else
snprint(buf, sizeof(buf), "/ppt/slide%d/image.%s", Slideno, ext);
mkfile(buf, p, n);
if(Debug)
print("#blip type=%s len=%d\n", ext, n);
}
void
pushtext(int type)
{
int n;
char *p, buf[64];
char *types[] = {
"title", "body", "notes", "unused", "other",
"centered-body", "centered-title", "half-body",
"quater-body"
};
p = fmtstrflush(Fp);
if (p == nil || *p == 0 || type < 0 || type >= nelem(types) || Slideno < 0){
free(p);
fmtstrinit(Fp);
return;
}
n = strlen(p);
if (Slideno == 0)
snprint(buf, sizeof(buf), "/ppt/master/%s", types[type]);
else
snprint(buf, sizeof(buf), "/ppt/slide%d/%s", Slideno, types[type]);
mkfile(buf, p, n);
fmtstrinit(Fp);
if(Debug)
print("#text name=%s len=%d\n#%s\n", buf, n, p);
}
int
decode(Hdr *h, int indent)
{
int bullet;
static int type;
/*
- Slide ID is really in the Slide Persist Atom unless the slide is from the
Template
- The slide sequence is listed under the latest 1000 record under 4080 and
the sequence of 1011 shows the list slides ... There is no real Slide
number that I can find.
*/
bullet = (type != 0 && type != 6);
switch (h->type){
case 1011:
slidepersist(h);
break;
case 1007:
slide(h);
break;
case 1009:
notes(h);
break;
case 3999:
textheaderatom(h, &type);
break;
case 4000:
textcharsatom(h, bullet);
pushtext(type);
break;
case 4008:
textbytesatom(h, bullet);
pushtext(type);
break;
case 4026:
cstring(h, bullet);
pushtext(type);
break;
case 4085:
useredit(h, indent+1);
break;
case 4086:
currentuser(h);
break;
case 6002:
persistincrement(h);
break;
case 0xf00b:
escheropt(h);
break;
case 0xf01a:
pushimage(h, "emf.gz");
break;
case 0xf01b:
pushimage(h, "wmf.gz");
break;
case 0xf01c:
pushimage(h, "pict.gz");
break;
case 0xf01d:
pushimage(h, "jpg");
break;
case 0xf01e:
pushimage(h, "png");
break;
case 0xf007:
blipstoreentry(h);
break;
default:
if (h->cont == Isdir)
return parse(-1, h->len, indent+1) +8;
skip(h, h->got);
break;
}
return h->len;
}
int
parse(vlong off, int len, int indent)
{
Hdr h;
int got;
if (off != -1 && Bseek(Bi, off, 0) != off)
sysfatal("seek failed");
got = 0;
do {
if(getrec(&h, indent) == -1)
return -1;
got += decode(&h, indent);
} while((len > 0 && got < len-9) || (len == 0 && got < h.got));
return got;
}
void
fsread(Req *r)
{
Vfile *vf;
vlong offset;
long count;
vf = r->fid->file->aux;
offset = r->ifcall.offset;
count = r->ifcall.count;
if(offset >= vf->len){
r->ofcall.count = 0;
respond(r, nil);
return;
}
if(offset+count >= vf->len)
count = vf->len - offset;
memmove(r->ofcall.data, vf->data+offset, count);
r->ofcall.count = count;
respond(r, nil);
}
void
usage(void)
{
fmtprint(Fp, "usage: %s [-ba] [-Dd] [-s srvname] [-m mtpt] [/mnt/doc/Current␣User "
"/mnt/doc/PowerPoint␣Document /mnt/doc/Pictures]\n", argv0);
exits("usage");
}
void
main(int argc, char **argv)
{
Qid q;
Img *im;
Blk *bk;
Xref *rf;
Fmt fmt;
int i, j, flags;
char *current, *document, *pictures, *srvname, *mtpt;
Fs.tree = alloctree("bill", "trog", DMDIR|0755, nil);
q = Fs.tree->root->qid;
Fp = &fmt;
mtpt = nil;
flags = 0;
srvname = nil;
current="/mnt/doc/Current␣User";
document="/mnt/doc/PowerPoint␣Document";
pictures="/mnt/doc/Pictures";
ARGBEGIN{
case 'D':
chatty9p++;
break;
case 'd':
Debug++;
break;
case 'b':
flags |= MBEFORE;
break;
case 'a':
flags |= MAFTER;
break;
case 's':
srvname = EARGF(usage());
break;
case 'm':
mtpt = EARGF(usage());
break;
default:
usage();
}ARGEND;
if (argc != 0 && argc != 3)
usage();
fmtstrinit(Fp);
if (Debug)
print("\nfile=%s\n", current);
if ((Bi = Bopen(current, OREAD)) == nil)
sysfatal("%s cannot open\n", current);
parse(0, 0, 0);
Bterm(Bi);
if (Debug)
print("\nfile=%s\n", document);
if ((Bi = Bopen(document, OREAD)) == nil)
sysfatal("%s cannot open\n", document);
parse(Start, 0, 0);
qsort(Blktab.table, Blktab.used, Blktab.size, cmp);
for(i = 0; (bk = indextab(&Blktab, i)); i++)
parse(bk->off, 0, 0);
Bterm(Bi);
if (Debug)
print("\nfile=%s\n", pictures);
if ((Bi = Bopen(pictures, OREAD)) != nil){
for(i = 0; (im = indextab(&Imgtab, i)) != nil; i++)
for(j = 0; (rf = indextab(&Xreftab, j)) != nil; j++){
if (rf->blip -1 == i){
Slideno = rf->slide;
parse(im->off, 0, 0);
}
}
Bterm(Bi);
}
if (mtpt == nil)
mtpt = "/mnt/doc/";
if (flags == 0)
flags = MBEFORE;
postmountsrv(&Fs, srvname, mtpt, flags);
exits(0);
}
|