msexceltables.c 15 KB

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