msexceltables.c 15 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848
  1. /*
  2. * This file is part of the UCB release of Plan 9. It is subject to the license
  3. * terms in the LICENSE file found in the top-level directory of this
  4. * distribution and at http://akaros.cs.berkeley.edu/files/Plan9License. No
  5. * part of the UCB release of Plan 9, including this file, may be copied,
  6. * modified, propagated, or distributed except according to the terms contained
  7. * in the LICENSE file.
  8. */
  9. /* msexceltables.c Steve Simon 5-Jan-2005 */
  10. #include <u.h>
  11. #include <libc.h>
  12. #include <bio.h>
  13. #include <ctype.h>
  14. enum {
  15. Tillegal = 0,
  16. Tnumber, // cell types
  17. Tlabel,
  18. Tindex,
  19. Tbool,
  20. Terror,
  21. Ver8 = 0x600, // only BIFF8 and BIFF8x files support unicode
  22. Nwidths = 4096,
  23. };
  24. typedef struct Biff Biff;
  25. typedef struct Col Col;
  26. typedef struct Row Row;
  27. struct Row {
  28. Row *next; // next row
  29. int r; // row number
  30. Col *col; // list of cols in row
  31. };
  32. struct Col {
  33. Col *next; // next col in row
  34. int c; // col number
  35. int f; // index into formating table (Xf)
  36. int type; // type of value for union below
  37. union { // value
  38. int index; // index into string table (Strtab)
  39. int error;
  40. int bool;
  41. char *label;
  42. double number;
  43. };
  44. };
  45. struct Biff {
  46. Biobuf *bp; // input file
  47. int op; // current record type
  48. int len; // length of current record
  49. };
  50. // options
  51. static int Nopad = 0; // disable padding cells to colum width
  52. static int Trunc = 0; // truncate cells to colum width
  53. static int All = 0; // dump all sheet types, Worksheets only by default
  54. static char *Delim = " "; // field delimiter
  55. static char *Sheetrange = nil; // range of sheets wanted
  56. static char *Columnrange = nil; // range of collums wanted
  57. static int Debug = 0;
  58. // file scope
  59. static int Defwidth = 10; // default colum width if non given
  60. static int Biffver; // file vesion
  61. static int Datemode; // date ref: 1899-Dec-31 or 1904-jan-1
  62. static char **Strtab = nil; // label contents heap
  63. static int Nstrtab = 0; // # of above
  64. static int *Xf; // array of extended format indices
  65. static int Nxf = 0; // # of above
  66. static Biobuf *bo; // stdout (sic)
  67. static int Doquote = 1; // quote text fields if they are rc(1) unfriendly
  68. // table scope
  69. static int Width[Nwidths]; // array of colum widths
  70. static int Ncols = -1; // max colums in table used
  71. static int Content = 0; // type code for contents of sheet
  72. static Row *Root = nil; // one worksheet's worth of cells
  73. static char *Months[] = { "Jan", "Feb", "Mar", "Apr", "May", "Jun",
  74. "Jul", "Aug", "Sep", "Oct", "Nov", "Dec" };
  75. static char *Errmsgs[] = {
  76. [0x0] = "#NULL!", // intersection of two cell ranges is empty
  77. [0x7] = "#DIV/0!", // division by zero
  78. [0xf] = "#VALUE!", // wrong type of operand
  79. [0x17] = "#REF!", // illegal or deleted cell reference
  80. [0x1d] = "#NAME?", // wrong function or range name
  81. [0x24] = "#NUM!", // value range overflow
  82. [0x2a] = "#N/A!", // argument of function not available
  83. };
  84. int
  85. wanted(char *range, int here)
  86. {
  87. int n, s;
  88. char *p;
  89. if (! range)
  90. return 1;
  91. s = -1;
  92. p = range;
  93. while(1){
  94. n = strtol(p, &p, 10);
  95. switch(*p){
  96. case 0:
  97. if(n == here)
  98. return 1;
  99. if(s != -1 && here > s && here < n)
  100. return 1;
  101. return 0;
  102. case ',':
  103. if(n == here)
  104. return 1;
  105. if(s != -1 && here > s && here < n)
  106. return 1;
  107. s = -1;
  108. p++;
  109. break;
  110. case '-':
  111. if(n == here)
  112. return 1;
  113. s = n;
  114. p++;
  115. break;
  116. default:
  117. sysfatal("%s malformed range spec", range);
  118. break;
  119. }
  120. }
  121. }
  122. void
  123. cell(int r, int c, int f, int type, void *val)
  124. {
  125. Row *row, *nrow;
  126. Col *col, *ncol;
  127. if(c > Ncols)
  128. Ncols = c;
  129. if((ncol = malloc(sizeof(Col))) == nil)
  130. sysfatal("no memory");
  131. ncol->c = c;
  132. ncol->f = f;
  133. ncol->type = type;
  134. ncol->next = nil;
  135. switch(type){
  136. case Tnumber: ncol->number = *(double *)val; break;
  137. case Tlabel: ncol->label = (char *)val; break;
  138. case Tindex: ncol->index = *(int *)val; break;
  139. case Tbool: ncol->bool = *(int *)val; break;
  140. case Terror: ncol->error = *(int *)val; break;
  141. default: sysfatal("can't happen error");
  142. }
  143. if(Root == nil || Root->r > r){
  144. if((nrow = malloc(sizeof(Row))) == nil)
  145. sysfatal("no memory");
  146. nrow->col = ncol;
  147. ncol->next = nil;
  148. nrow->r = r;
  149. nrow->next = Root;
  150. Root = nrow;
  151. return;
  152. }
  153. for(row = Root; row; row = row->next){
  154. if(row->r == r){
  155. if(row->col->c > c){
  156. ncol->next = row->col;
  157. row->col = ncol;
  158. return;
  159. }
  160. else{
  161. for(col = row->col; col; col = col->next)
  162. if(col->next == nil || col->next->c > c){
  163. ncol->next = col->next;
  164. col->next = ncol;
  165. return;
  166. }
  167. }
  168. }
  169. if(row->next == nil || row->next->r > r){
  170. if((nrow = malloc(sizeof(Row))) == nil)
  171. sysfatal("no memory");
  172. nrow->col = ncol;
  173. nrow->r = r;
  174. nrow->next = row->next;
  175. row->next = nrow;
  176. return;
  177. }
  178. }
  179. sysfatal("cannot happen error");
  180. }
  181. struct Tm *
  182. bifftime(double t)
  183. {
  184. /* Beware - These epochs are wrong, this
  185. * is due to Excel still remaining compatible
  186. * with Lotus-123, which incorrectly believed 1900
  187. * was a leap year
  188. */
  189. if(Datemode)
  190. t -= 24107; // epoch = 1/1/1904
  191. else
  192. t -= 25569; // epoch = 31/12/1899
  193. t *= 60*60*24;
  194. return localtime((int32_t)t);
  195. }
  196. void
  197. numfmt(int fmt, int min, int max, double num)
  198. {
  199. char buf[1024];
  200. struct Tm *tm;
  201. if(fmt == 9)
  202. snprint(buf, sizeof(buf),"%.0f%%", num);
  203. else
  204. if(fmt == 10)
  205. snprint(buf, sizeof(buf),"%f%%", num);
  206. else
  207. if(fmt == 11 || fmt == 48)
  208. snprint(buf, sizeof(buf),"%e", num);
  209. else
  210. if(fmt >= 14 && fmt <= 17){
  211. tm = bifftime(num);
  212. snprint(buf, sizeof(buf),"%d-%s-%d",
  213. tm->mday, Months[tm->mon], tm->year+1900);
  214. }
  215. else
  216. if((fmt >= 18 && fmt <= 21) || (fmt >= 45 && fmt <= 47)){
  217. tm = bifftime(num);
  218. snprint(buf, sizeof(buf),"%02d:%02d:%02d", tm->hour, tm->min, tm->sec);
  219. }
  220. else
  221. if(fmt == 22){
  222. tm = bifftime(num);
  223. snprint(buf, sizeof(buf),"%02d:%02d:%02d %d-%s-%d",
  224. tm->hour, tm->min, tm->sec,
  225. tm->mday, Months[tm->mon], tm->year+1900);
  226. }else
  227. snprint(buf, sizeof(buf),"%g", num);
  228. Bprint(bo, "%-*.*q", min, max, buf);
  229. }
  230. void
  231. dump(void)
  232. {
  233. Row *r;
  234. Col *c, *c1;
  235. char *strfmt;
  236. int i, n, last, min, max;
  237. if(Doquote)
  238. strfmt = "%-*.*q";
  239. else
  240. strfmt = "%-*.*s";
  241. for(r = Root; r; r = r->next){
  242. n = 1;
  243. for(c = r->col; c; c = c->next){
  244. n++;
  245. if(! wanted(Columnrange, n))
  246. continue;
  247. if(c->c < 0 || c->c >= Nwidths || (min = Width[c->c]) == 0)
  248. min = Defwidth;
  249. if((c->next && c->c == c->next->c) || Nopad)
  250. min = 0;
  251. max = -1;
  252. if(Trunc && min > 2)
  253. max = min -2; // FIXME: -2 because of bug %q format ?
  254. switch(c->type){
  255. case Tnumber:
  256. if(Xf == nil || Xf[c->f] == 0)
  257. Bprint(bo, "%-*.*g", min, max, c->number);
  258. else
  259. numfmt(Xf[c->f], min, max, c->number);
  260. break;
  261. case Tlabel:
  262. Bprint(bo, strfmt, min, max, c->label);
  263. break;
  264. case Tbool:
  265. Bprint(bo, strfmt, min, max, (c->bool)? "True": "False");
  266. break;
  267. case Tindex:
  268. if(c->index < 0 || c->index >= Nstrtab)
  269. sysfatal("SST string out of range - corrupt file?");
  270. Bprint(bo, strfmt, min, max, Strtab[c->index]);
  271. break;
  272. case Terror:
  273. if(c->error < 0 || c->error >= nelem(Errmsgs) || !Errmsgs[c->error])
  274. Bprint(bo, "#ERR=%d", c->index);
  275. else
  276. Bprint(bo, strfmt, min, max, Errmsgs[c->error]);
  277. break;
  278. default:
  279. sysfatal("cannot happen error");
  280. break;
  281. }
  282. last = 1;
  283. for(i = n+1, c1 = c->next; c1; c1 = c1->next, i++)
  284. if(wanted(Columnrange, i)){
  285. last = 0;
  286. break;
  287. }
  288. if(! last){
  289. if(c->next->c == c->c) // bar charts
  290. Bprint(bo, "=");
  291. else{
  292. Bprint(bo, "%s", Delim);
  293. for(i = c->c; c->next && i < c->next->c -1; i++)
  294. Bprint(bo, "%-*.*s%s", min, max, "", Delim);
  295. }
  296. }
  297. }
  298. if(r->next)
  299. for(i = r->r; i < r->next->r; i++)
  300. Bprint(bo, "\n");
  301. }
  302. Bprint(bo, "\n");
  303. }
  304. void
  305. release(void)
  306. {
  307. Row *r, *or;
  308. Col *c, *oc;
  309. r = Root;
  310. while(r){
  311. c = r->col;
  312. while(c){
  313. if(c->type == Tlabel)
  314. free(c->label);
  315. oc = c;
  316. c = c->next;
  317. free(oc);
  318. }
  319. or = r;
  320. r = r->next;
  321. free(or);
  322. }
  323. Root = nil;
  324. memset(Width, 0, sizeof(Width));
  325. Ncols = -1;
  326. }
  327. void
  328. skip(Biff *b, int len)
  329. {
  330. assert(len <= b->len);
  331. if(Bseek(b->bp, len, 1) == -1)
  332. sysfatal("seek failed - %r");
  333. b->len -= len;
  334. }
  335. void
  336. gmem(Biff *b, void *p, int n)
  337. {
  338. if(b->len < n)
  339. sysfatal("short record %d < %d", b->len, n);
  340. if(Bread(b->bp, p, n) != n)
  341. sysfatal("unexpected EOF - %r");
  342. b->len -= n;
  343. }
  344. void
  345. xd(Biff *b)
  346. {
  347. uint64_t off;
  348. uint8_t buf[16];
  349. int addr, got, n, i, j;
  350. addr = 0;
  351. off = Boffset(b->bp);
  352. while(addr < b->len){
  353. n = (b->len >= sizeof(buf))? sizeof(buf): b->len;
  354. got = Bread(b->bp, buf, n);
  355. Bprint(bo, " %6d ", addr);
  356. addr += n;
  357. for(i = 0; i < got; i++)
  358. Bprint(bo, "%02x ", buf[i]);
  359. for(j = i; j < 16; j++)
  360. Bprint(bo, " ");
  361. Bprint(bo, " ");
  362. for(i = 0; i < got; i++)
  363. Bprint(bo, "%c", isprint(buf[i])? buf[i]: '.');
  364. Bprint(bo, "\n");
  365. }
  366. Bseek(b->bp, off, 0);
  367. }
  368. static int
  369. getrec(Biff *b)
  370. {
  371. int c;
  372. if((c = Bgetc(b->bp)) == -1)
  373. return -1; // real EOF
  374. b->op = c;
  375. if((c = Bgetc(b->bp)) == -1)
  376. sysfatal("unexpected EOF - %r");
  377. b->op |= c << 8;
  378. if((c = Bgetc(b->bp)) == -1)
  379. sysfatal("unexpected EOF - %r");
  380. b->len = c;
  381. if((c = Bgetc(b->bp)) == -1)
  382. sysfatal("unexpected EOF - %r");
  383. b->len |= c << 8;
  384. if(b->op == 0 && b->len == 0)
  385. return -1;
  386. if(Debug){
  387. Bprint(bo, "op=0x%x len=%d\n", b->op, b->len);
  388. xd(b);
  389. }
  390. return 0;
  391. }
  392. static uint64_t
  393. gint(Biff *b, int n)
  394. {
  395. int i, c;
  396. uint64_t vl, rc;
  397. if(b->len < n)
  398. return -1;
  399. rc = 0;
  400. for(i = 0; i < n; i++){
  401. if((c = Bgetc(b->bp)) == -1)
  402. sysfatal("unexpected EOF - %r");
  403. b->len--;
  404. vl = c;
  405. rc |= vl << (8*i);
  406. }
  407. return rc;
  408. }
  409. double
  410. grk(Biff *b)
  411. {
  412. int f;
  413. uint64_t n;
  414. double d;
  415. n = gint(b, 4);
  416. f = n & 3;
  417. n &= ~3LL;
  418. if(f & 2){
  419. d = n / 4.0;
  420. }
  421. else{
  422. n <<= 32;
  423. memcpy(&d, &n, sizeof(d));
  424. }
  425. if(f & 1)
  426. d /= 100.0;
  427. return d;
  428. }
  429. double
  430. gdoub(Biff *b)
  431. {
  432. double d;
  433. uint64_t n = gint(b, 8);
  434. memcpy(&d, &n, sizeof(n));
  435. return d;
  436. }
  437. char *
  438. gstr(Biff *b, int len_width)
  439. {
  440. Rune r;
  441. char *buf, *p;
  442. int nch, w, ap, ln, rt, opt;
  443. enum {
  444. Unicode = 1,
  445. Asian_phonetic = 4,
  446. Rich_text = 8,
  447. };
  448. if(b->len < len_width){
  449. if(getrec(b) == -1)
  450. sysfatal("starting STRING expected CONTINUE, got EOF");
  451. if(b->op != 0x03c)
  452. sysfatal("starting STRING expected CONTINUE, got op=0x%x", b->op);
  453. }
  454. ln = gint(b, len_width);
  455. if(Biffver != Ver8){
  456. if((buf = calloc(ln+1, sizeof(char))) == nil)
  457. sysfatal("no memory");
  458. gmem(b, buf, ln);
  459. return buf;
  460. }
  461. if((buf = calloc(ln+1, sizeof(char)*UTFmax)) == nil)
  462. sysfatal("no memory");
  463. p = buf;
  464. if(ln == 0)
  465. return buf;
  466. nch = 0;
  467. *buf = 0;
  468. opt = gint(b, 1);
  469. if(opt & Rich_text)
  470. rt = gint(b, 2);
  471. else
  472. rt = 0;
  473. if(opt & Asian_phonetic)
  474. ap = gint(b, 4);
  475. else
  476. ap = 0;
  477. for(;;){
  478. w = (opt & Unicode)? sizeof(Rune): sizeof(char);
  479. while(b->len > 0){
  480. r = gint(b, w);
  481. p += runetochar(p, &r);
  482. if(++nch >= ln){
  483. if(rt)
  484. skip(b, rt*4);
  485. if(ap)
  486. skip(b, ap);
  487. return buf;
  488. }
  489. }
  490. if(getrec(b) == -1)
  491. sysfatal("in STRING expected CONTINUE, got EOF");
  492. if(b->op != 0x03c)
  493. sysfatal("in STRING expected CONTINUE, got op=0x%x", b->op);
  494. opt = gint(b, 1);
  495. }
  496. }
  497. void
  498. sst(Biff *b)
  499. {
  500. int n;
  501. skip(b, 4); // total # strings
  502. Nstrtab = gint(b, 4); // # unique strings
  503. if((Strtab = calloc(Nstrtab, sizeof(char *))) == nil)
  504. sysfatal("no memory");
  505. for(n = 0; n < Nstrtab; n++)
  506. Strtab[n] = gstr(b, 2);
  507. }
  508. void
  509. boolerr(Biff *b)
  510. {
  511. int r = gint(b, 2); // row
  512. int c = gint(b, 2); // col
  513. int f = gint(b, 2); // formatting ref
  514. int v = gint(b, 1); // bool value / err code
  515. int t = gint(b, 1); // type
  516. cell(r, c, f, (t)? Terror: Tbool, &v);
  517. }
  518. void
  519. rk(Biff *b)
  520. {
  521. int r = gint(b, 2); // row
  522. int c = gint(b, 2); // col
  523. int f = gint(b, 2); // formatting ref
  524. double v = grk(b); // value
  525. cell(r, c, f, Tnumber, &v);
  526. }
  527. void
  528. mulrk(Biff *b)
  529. {
  530. int r = gint(b, 2); // row
  531. int c = gint(b, 2); // first col
  532. while(b->len >= 6){
  533. int f = gint(b, 2); // formatting ref
  534. double v = grk(b); // value
  535. cell(r, c++, f, Tnumber, &v);
  536. }
  537. }
  538. void
  539. number(Biff *b)
  540. {
  541. int r = gint(b, 2); // row
  542. int c = gint(b, 2); // col
  543. int f = gint(b, 2); // formatting ref
  544. double v = gdoub(b); // double
  545. cell(r, c, f, Tnumber, &v);
  546. }
  547. void
  548. label(Biff *b)
  549. {
  550. int r = gint(b, 2); // row
  551. int c = gint(b, 2); // col
  552. int f = gint(b, 2); // formatting ref
  553. char *s = gstr(b, 2); // byte string
  554. cell(r, c, f, Tlabel, s);
  555. }
  556. void
  557. labelsst(Biff *b)
  558. {
  559. int r = gint(b, 2); // row
  560. int c = gint(b, 2); // col
  561. int f = gint(b, 2); // formatting ref
  562. int i = gint(b, 2); // sst string ref
  563. cell(r, c, f, Tindex, &i);
  564. }
  565. void
  566. bof(Biff *b)
  567. {
  568. Biffver = gint(b, 2);
  569. Content = gint(b, 2);
  570. }
  571. void
  572. defcolwidth(Biff *b)
  573. {
  574. Defwidth = gint(b, 2);
  575. }
  576. void
  577. datemode(Biff *b)
  578. {
  579. Datemode = gint(b, 2);
  580. }
  581. void
  582. eof(Biff *b)
  583. {
  584. int i;
  585. struct {
  586. int n;
  587. char *s;
  588. } names[] = {
  589. {0x005, "Workbook globals"},
  590. {0x006, "Visual Basic module"},
  591. {0x010, "Worksheet"},
  592. {0x020, "Chart"},
  593. {0x040, "Macro sheet"},
  594. {0x100, "Workspace file"},
  595. };
  596. static int sheet = 0;
  597. if(! wanted(Sheetrange, ++sheet)){
  598. release();
  599. return;
  600. }
  601. if(Ncols != -1){
  602. if(All){
  603. for(i = 0; i < nelem(names); i++)
  604. if(names[i].n == Content){
  605. Bprint(bo, "\n# contents %s\n", names[i].s);
  606. dump();
  607. }
  608. }
  609. else
  610. if(Content == 0x10) // Worksheet
  611. dump();
  612. }
  613. release();
  614. USED(b);
  615. }
  616. void
  617. colinfo(Biff *b)
  618. {
  619. int c;
  620. int c1 = gint(b, 2);
  621. int c2 = gint(b, 2);
  622. int w = gint(b, 2);
  623. if(c1 < 0)
  624. sysfatal("negative column number (%d)", c1);
  625. if(c2 >= Nwidths)
  626. sysfatal("too many columns (%d > %d)", c2, Nwidths);
  627. w /= 256;
  628. if(w > 100)
  629. w = 100;
  630. if(w < 0)
  631. w = 0;
  632. for(c = c1; c <= c2; c++)
  633. Width[c] = w;
  634. }
  635. void
  636. xf(Biff *b)
  637. {
  638. int fmt;
  639. static int nalloc = 0;
  640. skip(b, 2);
  641. fmt = gint(b, 2);
  642. if(nalloc >= Nxf){
  643. nalloc += 20;
  644. if((Xf = realloc(Xf, nalloc*sizeof(int))) == nil)
  645. sysfatal("no memory");
  646. }
  647. Xf[Nxf++] = fmt;
  648. }
  649. void
  650. writeaccess(Biff *b)
  651. {
  652. Bprint(bo, "# author %s\n", gstr(b, 2));
  653. }
  654. void
  655. codepage(Biff *b)
  656. {
  657. int codepage = gint(b, 2);
  658. if(codepage != 1200) // 1200 == UTF-16
  659. Bprint(bo, "# codepage %d\n", codepage);
  660. }
  661. void
  662. xls2csv(Biobuf *bp)
  663. {
  664. int i;
  665. Biff biff, *b;
  666. struct {
  667. int op;
  668. void (*func)(Biff *);
  669. } dispatch[] = {
  670. {0x000a, eof},
  671. {0x0022, datemode},
  672. {0x0042, codepage},
  673. {0x0055, defcolwidth},
  674. {0x005c, writeaccess},
  675. {0x007d, colinfo},
  676. {0x00bd, mulrk},
  677. {0x00fc, sst},
  678. {0x00fd, labelsst},
  679. {0x0203, number},
  680. {0x0204, label},
  681. {0x0205, boolerr},
  682. {0x027e, rk},
  683. {0x0809, bof},
  684. {0x00e0, xf},
  685. };
  686. b = &biff;
  687. b->bp = bp;
  688. while(getrec(b) != -1){
  689. for(i = 0; i < nelem(dispatch); i++)
  690. if(b->op == dispatch[i].op)
  691. (*dispatch[i].func)(b);
  692. skip(b, b->len);
  693. }
  694. }
  695. void
  696. usage(void)
  697. {
  698. fprint(2, "usage: %s [-Danqt] [-w worksheets] [-c columns] [-d delim] /mnt/doc/Workbook\n", argv0);
  699. exits("usage");
  700. }
  701. void
  702. main(int argc, char *argv[])
  703. {
  704. int i;
  705. Biobuf bin, bout, *bp;
  706. ARGBEGIN{
  707. case 'D':
  708. Debug = 1;
  709. break;
  710. case 'a':
  711. All = 1;
  712. break;
  713. case 'q':
  714. Doquote = 0;
  715. break;
  716. case 'd':
  717. Delim = EARGF(usage());
  718. break;
  719. case 'n':
  720. Nopad = 1;
  721. break;
  722. case 't':
  723. Trunc = 1;
  724. break;
  725. case 'c':
  726. Columnrange = EARGF(usage());
  727. break;
  728. case 'w':
  729. Sheetrange = EARGF(usage());
  730. break;
  731. default:
  732. usage();
  733. break;
  734. }ARGEND;
  735. if(argc != 1)
  736. usage();
  737. bo = &bout;
  738. quotefmtinstall();
  739. Binit(bo, OWRITE, 1);
  740. if(argc > 0) {
  741. for(i = 0; i < argc; i++){
  742. if((bp = Bopen(argv[i], OREAD)) == nil)
  743. sysfatal("%s cannot open - %r", argv[i]);
  744. xls2csv(bp);
  745. Bterm(bp);
  746. }
  747. } else {
  748. Binit(&bin, 0, OREAD);
  749. xls2csv(&bin);
  750. }
  751. exits(0);
  752. }