1114402Sru// -*- C++ -*- 2151497Sru/* Copyright (C) 1994, 2000, 2001, 2002, 2003, 2004 3151497Sru Free Software Foundation, Inc. 4114402Sru Written by James Clark (jjc@jclark.com) 5114402Sru 6114402SruThis file is part of groff. 7114402Sru 8114402Srugroff is free software; you can redistribute it and/or modify it under 9114402Sruthe terms of the GNU General Public License as published by the Free 10114402SruSoftware Foundation; either version 2, or (at your option) any later 11114402Sruversion. 12114402Sru 13114402Srugroff is distributed in the hope that it will be useful, but WITHOUT ANY 14114402SruWARRANTY; without even the implied warranty of MERCHANTABILITY or 15114402SruFITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License 16114402Srufor more details. 17114402Sru 18114402SruYou should have received a copy of the GNU General Public License along 19114402Sruwith groff; see the file COPYING. If not, write to the Free Software 20151497SruFoundation, 51 Franklin St - Fifth Floor, Boston, MA 02110-1301, USA. */ 21114402Sru 22114402Sru/* 23114402SruTODO 24114402Sru 25114402Sruoption to use beziers for circle/ellipse/arc 26114402Sruoption to use lines for spline (for LJ3) 27114402Sruleft/top offset registration 28114402Sruoutput bin selection option 29114402Srupaper source option 30114402Sruoutput non-integer parameters using fixed point numbers 31114402SruX command to insert contents of file 32114402SruX command to specify inline escape sequence (how to specify unprintable chars?) 33114402SruX command to include bitmap graphics 34114402Sru*/ 35114402Sru 36114402Sru#include "driver.h" 37114402Sru#include "nonposix.h" 38114402Sru 39114402Sruextern "C" const char *Version_string; 40114402Sru 41114402Srustatic struct { 42114402Sru const char *name; 43114402Sru int code; 44114402Sru // at 300dpi 45114402Sru int x_offset_portrait; 46114402Sru int x_offset_landscape; 47114402Sru} paper_table[] = { 48114402Sru { "letter", 2, 75, 60 }, 49114402Sru { "legal", 3, 75, 60 }, 50114402Sru { "executive", 1, 75, 60 }, 51114402Sru { "a4", 26, 71, 59 }, 52114402Sru { "com10", 81, 75, 60 }, 53114402Sru { "monarch", 80, 75, 60 }, 54114402Sru { "c5", 91, 71, 59 }, 55114402Sru { "b5", 100, 71, 59 }, 56114402Sru { "dl", 90, 71, 59 }, 57114402Sru}; 58114402Sru 59114402Srustatic int user_paper_size = -1; 60114402Srustatic int landscape_flag = 0; 61114402Srustatic int duplex_flag = 0; 62114402Sru 63114402Sru// An upper limit on the paper size in centipoints, 64114402Sru// used for setting HPGL picture frame. 65114402Sru#define MAX_PAPER_WIDTH (12*720) 66114402Sru#define MAX_PAPER_HEIGHT (17*720) 67114402Sru 68114402Sru// Dotted lines that are thinner than this don't work right. 69114402Sru#define MIN_DOT_PEN_WIDTH .351 70114402Sru 71114402Sru#ifndef DEFAULT_LINE_WIDTH_FACTOR 72114402Sru// in ems/1000 73114402Sru#define DEFAULT_LINE_WIDTH_FACTOR 40 74114402Sru#endif 75114402Sru 76114402Sruconst int DEFAULT_HPGL_UNITS = 1016; 77114402Sruint line_width_factor = DEFAULT_LINE_WIDTH_FACTOR; 78114402Sruunsigned ncopies = 0; // 0 means don't send ncopies command 79114402Sru 80114402Srustatic int lookup_paper_size(const char *); 81114402Sru 82114402Sruclass lj4_font : public font { 83114402Srupublic: 84114402Sru ~lj4_font(); 85114402Sru void handle_unknown_font_command(const char *command, const char *arg, 86114402Sru const char *filename, int lineno); 87114402Sru static lj4_font *load_lj4_font(const char *); 88114402Sru int weight; 89114402Sru int style; 90114402Sru int proportional; 91114402Sru int typeface; 92114402Sruprivate: 93114402Sru lj4_font(const char *); 94114402Sru}; 95114402Sru 96114402Srulj4_font::lj4_font(const char *nm) 97114402Sru: font(nm), weight(0), style(0), proportional(0), typeface(0) 98114402Sru{ 99114402Sru} 100114402Sru 101114402Srulj4_font::~lj4_font() 102114402Sru{ 103114402Sru} 104114402Sru 105114402Srulj4_font *lj4_font::load_lj4_font(const char *s) 106114402Sru{ 107114402Sru lj4_font *f = new lj4_font(s); 108114402Sru if (!f->load()) { 109114402Sru delete f; 110114402Sru return 0; 111114402Sru } 112114402Sru return f; 113114402Sru} 114114402Sru 115114402Srustatic struct { 116114402Sru const char *s; 117114402Sru int lj4_font::*ptr; 118114402Sru int min; 119114402Sru int max; 120114402Sru} command_table[] = { 121114402Sru { "pclweight", &lj4_font::weight, -7, 7 }, 122114402Sru { "pclstyle", &lj4_font::style, 0, 32767 }, 123114402Sru { "pclproportional", &lj4_font::proportional, 0, 1 }, 124114402Sru { "pcltypeface", &lj4_font::typeface, 0, 65535 }, 125114402Sru}; 126114402Sru 127114402Sruvoid lj4_font::handle_unknown_font_command(const char *command, 128114402Sru const char *arg, 129114402Sru const char *filename, int lineno) 130114402Sru{ 131114402Sru for (unsigned int i = 0; 132114402Sru i < sizeof(command_table)/sizeof(command_table[0]); i++) { 133114402Sru if (strcmp(command, command_table[i].s) == 0) { 134114402Sru if (arg == 0) 135114402Sru fatal_with_file_and_line(filename, lineno, 136114402Sru "`%1' command requires an argument", 137114402Sru command); 138114402Sru char *ptr; 139114402Sru long n = strtol(arg, &ptr, 10); 140114402Sru if (n == 0 && ptr == arg) 141114402Sru fatal_with_file_and_line(filename, lineno, 142114402Sru "`%1' command requires numeric argument", 143114402Sru command); 144114402Sru if (n < command_table[i].min) { 145114402Sru error_with_file_and_line(filename, lineno, 146114402Sru "argument for `%1' command must not be less than %2", 147114402Sru command, command_table[i].min); 148114402Sru n = command_table[i].min; 149114402Sru } 150114402Sru else if (n > command_table[i].max) { 151114402Sru error_with_file_and_line(filename, lineno, 152114402Sru "argument for `%1' command must not be greater than %2", 153114402Sru command, command_table[i].max); 154114402Sru n = command_table[i].max; 155114402Sru } 156114402Sru this->*command_table[i].ptr = int(n); 157114402Sru break; 158114402Sru } 159114402Sru } 160114402Sru} 161114402Sru 162114402Sruclass lj4_printer : public printer { 163114402Srupublic: 164114402Sru lj4_printer(int); 165114402Sru ~lj4_printer(); 166114402Sru void set_char(int, font *, const environment *, int, const char *name); 167114402Sru void draw(int code, int *p, int np, const environment *env); 168114402Sru void begin_page(int); 169114402Sru void end_page(int page_length); 170114402Sru font *make_font(const char *); 171114402Sru void end_of_line(); 172114402Sruprivate: 173114402Sru void set_line_thickness(int size, int dot = 0); 174114402Sru void hpgl_init(); 175114402Sru void hpgl_start(); 176114402Sru void hpgl_end(); 177114402Sru int moveto(int hpos, int vpos); 178114402Sru int moveto1(int hpos, int vpos); 179114402Sru 180114402Sru int cur_hpos; 181114402Sru int cur_vpos; 182114402Sru lj4_font *cur_font; 183114402Sru int cur_size; 184114402Sru unsigned short cur_symbol_set; 185114402Sru int x_offset; 186114402Sru int line_thickness; 187114402Sru double pen_width; 188114402Sru double hpgl_scale; 189114402Sru int hpgl_inited; 190114402Sru int paper_size; 191114402Sru}; 192114402Sru 193114402Sruinline 194114402Sruint lj4_printer::moveto(int hpos, int vpos) 195114402Sru{ 196114402Sru if (cur_hpos != hpos || cur_vpos != vpos || cur_hpos < 0) 197114402Sru return moveto1(hpos, vpos); 198114402Sru else 199114402Sru return 1; 200114402Sru} 201114402Sru 202114402Sruinline 203114402Sruvoid lj4_printer::hpgl_start() 204114402Sru{ 205114402Sru fputs("\033%1B", stdout); 206114402Sru} 207114402Sru 208114402Sruinline 209114402Sruvoid lj4_printer::hpgl_end() 210114402Sru{ 211114402Sru fputs(";\033%0A", stdout); 212114402Sru} 213114402Sru 214114402Srulj4_printer::lj4_printer(int ps) 215114402Sru: cur_hpos(-1), 216114402Sru cur_font(0), 217114402Sru cur_size(0), 218114402Sru cur_symbol_set(0), 219114402Sru line_thickness(-1), 220114402Sru pen_width(-1.0), 221114402Sru hpgl_inited(0) 222114402Sru{ 223114402Sru if (7200 % font::res != 0) 224114402Sru fatal("invalid resolution %1: resolution must be a factor of 7200", 225114402Sru font::res); 226114402Sru fputs("\033E", stdout); // reset 227114402Sru if (font::res != 300) 228114402Sru printf("\033&u%dD", font::res); // unit of measure 229114402Sru if (ncopies > 0) 230114402Sru printf("\033&l%uX", ncopies); 231114402Sru paper_size = 0; // default to letter 232114402Sru if (font::papersize) { 233114402Sru int n = lookup_paper_size(font::papersize); 234114402Sru if (n < 0) 235114402Sru error("unknown paper size `%1'", font::papersize); 236114402Sru else 237114402Sru paper_size = n; 238114402Sru } 239114402Sru if (ps >= 0) 240114402Sru paper_size = ps; 241114402Sru printf("\033&l%dA" // paper size 242114402Sru "\033&l%dO" // orientation 243114402Sru "\033&l0E", // no top margin 244114402Sru paper_table[paper_size].code, 245114402Sru landscape_flag != 0); 246114402Sru if (landscape_flag) 247114402Sru x_offset = paper_table[paper_size].x_offset_landscape; 248114402Sru else 249114402Sru x_offset = paper_table[paper_size].x_offset_portrait; 250114402Sru x_offset = (x_offset * font::res) / 300; 251114402Sru if (duplex_flag) 252114402Sru printf("\033&l%dS", duplex_flag); 253114402Sru} 254114402Sru 255114402Srulj4_printer::~lj4_printer() 256114402Sru{ 257114402Sru fputs("\033E", stdout); 258114402Sru} 259114402Sru 260114402Sruvoid lj4_printer::begin_page(int) 261114402Sru{ 262114402Sru} 263114402Sru 264114402Sruvoid lj4_printer::end_page(int) 265114402Sru{ 266114402Sru putchar('\f'); 267114402Sru cur_hpos = -1; 268114402Sru} 269114402Sru 270114402Sruvoid lj4_printer::end_of_line() 271114402Sru{ 272114402Sru cur_hpos = -1; // force absolute motion 273114402Sru} 274114402Sru 275114402Sruinline 276114402Sruint is_unprintable(unsigned char c) 277114402Sru{ 278114402Sru return c < 32 && (c == 0 || (7 <= c && c <= 15) || c == 27); 279114402Sru} 280114402Sru 281151497Sruvoid lj4_printer::set_char(int idx, font *f, const environment *env, 282114402Sru int w, const char *) 283114402Sru{ 284151497Sru int code = f->get_code(idx); 285114402Sru 286114402Sru unsigned char ch = code & 0xff; 287114402Sru unsigned short symbol_set = code >> 8; 288114402Sru if (symbol_set != cur_symbol_set) { 289114402Sru printf("\033(%d%c", symbol_set/32, (symbol_set & 31) + 64); 290114402Sru cur_symbol_set = symbol_set; 291114402Sru } 292114402Sru if (f != cur_font) { 293114402Sru lj4_font *psf = (lj4_font *)f; 294114402Sru // FIXME only output those that are needed 295114402Sru printf("\033(s%dp%ds%db%dT", 296114402Sru psf->proportional, 297114402Sru psf->style, 298114402Sru psf->weight, 299114402Sru psf->typeface); 300114402Sru if (!psf->proportional || !cur_font || !cur_font->proportional) 301114402Sru cur_size = 0; 302114402Sru cur_font = psf; 303114402Sru } 304114402Sru if (env->size != cur_size) { 305114402Sru if (cur_font->proportional) { 306114402Sru static const char *quarters[] = { "", ".25", ".5", ".75" }; 307114402Sru printf("\033(s%d%sV", env->size/4, quarters[env->size & 3]); 308114402Sru } 309114402Sru else { 310114402Sru double pitch = double(font::res)/w; 311114402Sru // PCL uses the next largest pitch, so round it down. 312114402Sru pitch = floor(pitch*100.0)/100.0; 313114402Sru printf("\033(s%.2fH", pitch); 314114402Sru } 315114402Sru cur_size = env->size; 316114402Sru } 317114402Sru if (!moveto(env->hpos, env->vpos)) 318114402Sru return; 319114402Sru if (is_unprintable(ch)) 320114402Sru fputs("\033&p1X", stdout); 321114402Sru putchar(ch); 322114402Sru cur_hpos += w; 323114402Sru} 324114402Sru 325114402Sruint lj4_printer::moveto1(int hpos, int vpos) 326114402Sru{ 327114402Sru if (hpos < x_offset || vpos < 0) 328114402Sru return 0; 329114402Sru fputs("\033*p", stdout); 330114402Sru if (cur_hpos < 0) 331114402Sru printf("%dx%dY", hpos - x_offset, vpos); 332114402Sru else { 333114402Sru if (cur_hpos != hpos) 334114402Sru printf("%s%d%c", hpos > cur_hpos ? "+" : "", 335114402Sru hpos - cur_hpos, vpos == cur_vpos ? 'X' : 'x'); 336114402Sru if (cur_vpos != vpos) 337114402Sru printf("%s%dY", vpos > cur_vpos ? "+" : "", vpos - cur_vpos); 338114402Sru } 339114402Sru cur_hpos = hpos; 340114402Sru cur_vpos = vpos; 341114402Sru return 1; 342114402Sru} 343114402Sru 344114402Sruvoid lj4_printer::draw(int code, int *p, int np, const environment *env) 345114402Sru{ 346114402Sru switch (code) { 347114402Sru case 'R': 348114402Sru { 349114402Sru if (np != 2) { 350114402Sru error("2 arguments required for rule"); 351114402Sru break; 352114402Sru } 353114402Sru int hpos = env->hpos; 354114402Sru int vpos = env->vpos; 355114402Sru int hsize = p[0]; 356114402Sru int vsize = p[1]; 357114402Sru if (hsize < 0) { 358114402Sru hpos += hsize; 359114402Sru hsize = -hsize; 360114402Sru } 361114402Sru if (vsize < 0) { 362114402Sru vpos += vsize; 363114402Sru vsize = -vsize; 364114402Sru } 365114402Sru if (!moveto(hpos, vpos)) 366114402Sru return; 367114402Sru printf("\033*c%da%db0P", hsize, vsize); 368114402Sru break; 369114402Sru } 370114402Sru case 'l': 371114402Sru if (np != 2) { 372114402Sru error("2 arguments required for line"); 373114402Sru break; 374114402Sru } 375114402Sru hpgl_init(); 376114402Sru if (!moveto(env->hpos, env->vpos)) 377114402Sru return; 378114402Sru hpgl_start(); 379114402Sru set_line_thickness(env->size, p[0] == 0 && p[1] == 0); 380114402Sru printf("PD%d,%d", p[0], p[1]); 381114402Sru hpgl_end(); 382114402Sru break; 383114402Sru case 'p': 384114402Sru case 'P': 385114402Sru { 386114402Sru if (np & 1) { 387114402Sru error("even number of arguments required for polygon"); 388114402Sru break; 389114402Sru } 390114402Sru if (np == 0) { 391114402Sru error("no arguments for polygon"); 392114402Sru break; 393114402Sru } 394114402Sru hpgl_init(); 395114402Sru if (!moveto(env->hpos, env->vpos)) 396114402Sru return; 397114402Sru hpgl_start(); 398114402Sru if (code == 'p') 399114402Sru set_line_thickness(env->size); 400114402Sru printf("PMPD%d", p[0]); 401114402Sru for (int i = 1; i < np; i++) 402114402Sru printf(",%d", p[i]); 403114402Sru printf("PM2%cP", code == 'p' ? 'E' : 'F'); 404114402Sru hpgl_end(); 405114402Sru break; 406114402Sru } 407114402Sru case '~': 408114402Sru { 409114402Sru if (np & 1) { 410114402Sru error("even number of arguments required for spline"); 411114402Sru break; 412114402Sru } 413114402Sru if (np == 0) { 414114402Sru error("no arguments for spline"); 415114402Sru break; 416114402Sru } 417114402Sru hpgl_init(); 418114402Sru if (!moveto(env->hpos, env->vpos)) 419114402Sru return; 420114402Sru hpgl_start(); 421114402Sru set_line_thickness(env->size); 422114402Sru printf("PD%d,%d", p[0]/2, p[1]/2); 423114402Sru const int tnum = 2; 424114402Sru const int tden = 3; 425114402Sru if (np > 2) { 426114402Sru fputs("BR", stdout); 427114402Sru for (int i = 0; i < np - 2; i += 2) { 428114402Sru if (i != 0) 429114402Sru putchar(','); 430114402Sru printf("%d,%d,%d,%d,%d,%d", 431114402Sru (p[i]*tnum)/(2*tden), 432114402Sru (p[i + 1]*tnum)/(2*tden), 433114402Sru p[i]/2 + (p[i + 2]*(tden - tnum))/(2*tden), 434114402Sru p[i + 1]/2 + (p[i + 3]*(tden - tnum))/(2*tden), 435114402Sru (p[i] - p[i]/2) + p[i + 2]/2, 436114402Sru (p[i + 1] - p[i + 1]/2) + p[i + 3]/2); 437114402Sru } 438114402Sru } 439114402Sru printf("PR%d,%d", p[np - 2] - p[np - 2]/2, p[np - 1] - p[np - 1]/2); 440114402Sru hpgl_end(); 441114402Sru break; 442114402Sru } 443114402Sru case 'c': 444114402Sru case 'C': 445114402Sru // troff adds an extra argument to C 446114402Sru if (np != 1 && !(code == 'C' && np == 2)) { 447114402Sru error("1 argument required for circle"); 448114402Sru break; 449114402Sru } 450114402Sru hpgl_init(); 451114402Sru if (!moveto(env->hpos + p[0]/2, env->vpos)) 452114402Sru return; 453114402Sru hpgl_start(); 454114402Sru if (code == 'c') { 455114402Sru set_line_thickness(env->size); 456114402Sru printf("CI%d", p[0]/2); 457114402Sru } 458114402Sru else 459114402Sru printf("WG%d,0,360", p[0]/2); 460114402Sru hpgl_end(); 461114402Sru break; 462114402Sru case 'e': 463114402Sru case 'E': 464114402Sru if (np != 2) { 465114402Sru error("2 arguments required for ellipse"); 466114402Sru break; 467114402Sru } 468114402Sru hpgl_init(); 469114402Sru if (!moveto(env->hpos + p[0]/2, env->vpos)) 470114402Sru return; 471114402Sru hpgl_start(); 472114402Sru printf("SC0,%.4f,0,-%.4f,2", hpgl_scale * double(p[0])/p[1], hpgl_scale); 473114402Sru if (code == 'e') { 474114402Sru set_line_thickness(env->size); 475114402Sru printf("CI%d", p[1]/2); 476114402Sru } 477114402Sru else 478114402Sru printf("WG%d,0,360", p[1]/2); 479114402Sru printf("SC0,%.4f,0,-%.4f,2", hpgl_scale, hpgl_scale); 480114402Sru hpgl_end(); 481114402Sru break; 482114402Sru case 'a': 483114402Sru { 484114402Sru if (np != 4) { 485114402Sru error("4 arguments required for arc"); 486114402Sru break; 487114402Sru } 488114402Sru hpgl_init(); 489114402Sru if (!moveto(env->hpos, env->vpos)) 490114402Sru return; 491114402Sru hpgl_start(); 492114402Sru set_line_thickness(env->size); 493114402Sru double c[2]; 494114402Sru if (adjust_arc_center(p, c)) { 495114402Sru double sweep = ((atan2(p[1] + p[3] - c[1], p[0] + p[2] - c[0]) 496114402Sru - atan2(-c[1], -c[0])) 497114402Sru * 180.0/PI); 498114402Sru if (sweep > 0.0) 499114402Sru sweep -= 360.0; 500114402Sru printf("PDAR%d,%d,%f", int(c[0]), int(c[1]), sweep); 501114402Sru } 502114402Sru else 503114402Sru printf("PD%d,%d", p[0] + p[2], p[1] + p[3]); 504114402Sru hpgl_end(); 505114402Sru } 506114402Sru break; 507114402Sru case 'f': 508114402Sru if (np != 1 && np != 2) { 509114402Sru error("1 argument required for fill"); 510114402Sru break; 511114402Sru } 512114402Sru hpgl_init(); 513114402Sru hpgl_start(); 514114402Sru if (p[0] >= 0 && p[0] <= 1000) 515114402Sru printf("FT10,%d", p[0]/10); 516114402Sru hpgl_end(); 517114402Sru break; 518114402Sru case 'F': 519114402Sru // not implemented yet 520114402Sru break; 521114402Sru case 't': 522114402Sru { 523114402Sru if (np == 0) { 524114402Sru line_thickness = -1; 525114402Sru } 526114402Sru else { 527114402Sru // troff gratuitously adds an extra 0 528114402Sru if (np != 1 && np != 2) { 529114402Sru error("0 or 1 argument required for thickness"); 530114402Sru break; 531114402Sru } 532114402Sru line_thickness = p[0]; 533114402Sru } 534114402Sru break; 535114402Sru } 536114402Sru default: 537114402Sru error("unrecognised drawing command `%1'", char(code)); 538114402Sru break; 539114402Sru } 540114402Sru} 541114402Sru 542114402Sruvoid lj4_printer::hpgl_init() 543114402Sru{ 544114402Sru if (hpgl_inited) 545114402Sru return; 546114402Sru hpgl_inited = 1; 547114402Sru hpgl_scale = double(DEFAULT_HPGL_UNITS)/font::res; 548114402Sru printf("\033&f0S" // push position 549114402Sru "\033*p0x0Y" // move to 0,0 550114402Sru "\033*c%dx%dy0T" // establish picture frame 551114402Sru "\033%%1B" // switch to HPGL 552114402Sru "SP1SC0,%.4f,0,-%.4f,2IR0,100,0,100" // set up scaling 553114402Sru "LA1,4,2,4" // round line ends and joins 554114402Sru "PR" // relative plotting 555114402Sru "TR0" // opaque 556114402Sru ";\033%%1A" // back to PCL 557114402Sru "\033&f1S", // pop position 558114402Sru MAX_PAPER_WIDTH, MAX_PAPER_HEIGHT, 559114402Sru hpgl_scale, hpgl_scale); 560114402Sru} 561114402Sru 562114402Sruvoid lj4_printer::set_line_thickness(int size, int dot) 563114402Sru{ 564114402Sru double pw; 565114402Sru if (line_thickness < 0) 566114402Sru pw = (size * (line_width_factor * 25.4))/(font::sizescale * 72000.0); 567114402Sru else 568114402Sru pw = line_thickness*25.4/font::res; 569114402Sru if (dot && pw < MIN_DOT_PEN_WIDTH) 570114402Sru pw = MIN_DOT_PEN_WIDTH; 571114402Sru if (pw != pen_width) { 572114402Sru printf("PW%f", pw); 573114402Sru pen_width = pw; 574114402Sru } 575114402Sru} 576114402Sru 577114402Srufont *lj4_printer::make_font(const char *nm) 578114402Sru{ 579114402Sru return lj4_font::load_lj4_font(nm); 580114402Sru} 581114402Sru 582114402Sruprinter *make_printer() 583114402Sru{ 584114402Sru return new lj4_printer(user_paper_size); 585114402Sru} 586114402Sru 587114402Srustatic 588114402Sruint lookup_paper_size(const char *s) 589114402Sru{ 590114402Sru for (unsigned int i = 0; 591114402Sru i < sizeof(paper_table)/sizeof(paper_table[0]); i++) { 592114402Sru // FIXME Perhaps allow unique prefix. 593114402Sru if (strcasecmp(s, paper_table[i].name) == 0) 594114402Sru return i; 595114402Sru } 596114402Sru return -1; 597114402Sru} 598114402Sru 599114402Srustatic void usage(FILE *stream); 600114402Sru 601114402Sruextern "C" int optopt, optind; 602114402Sru 603114402Sruint main(int argc, char **argv) 604114402Sru{ 605114402Sru setlocale(LC_NUMERIC, "C"); 606114402Sru program_name = argv[0]; 607114402Sru static char stderr_buf[BUFSIZ]; 608114402Sru setbuf(stderr, stderr_buf); 609114402Sru int c; 610114402Sru static const struct option long_options[] = { 611114402Sru { "help", no_argument, 0, CHAR_MAX + 1 }, 612114402Sru { "version", no_argument, 0, 'v' }, 613114402Sru { NULL, 0, 0, 0 } 614114402Sru }; 615151497Sru while ((c = getopt_long(argc, argv, "c:d:F:I:lp:vw:", long_options, NULL)) 616114402Sru != EOF) 617114402Sru switch(c) { 618114402Sru case 'l': 619114402Sru landscape_flag = 1; 620114402Sru break; 621151497Sru case 'I': 622151497Sru // ignore include search path 623151497Sru break; 624114402Sru case ':': 625114402Sru if (optopt == 'd') { 626114402Sru fprintf(stderr, "duplex assumed to be long-side\n"); 627114402Sru duplex_flag = 1; 628114402Sru } else 629151497Sru fprintf(stderr, "option -%c requires an argument\n", optopt); 630114402Sru fflush(stderr); 631114402Sru break; 632114402Sru case 'd': 633114402Sru if (!isdigit(*optarg)) // this ugly hack prevents -d without 634114402Sru optind--; // args from messing up the arg list 635114402Sru duplex_flag = atoi(optarg); 636114402Sru if (duplex_flag != 1 && duplex_flag != 2) { 637114402Sru fprintf(stderr, "odd value for duplex; assumed to be long-side\n"); 638114402Sru duplex_flag = 1; 639114402Sru } 640114402Sru break; 641114402Sru case 'p': 642114402Sru { 643114402Sru int n = lookup_paper_size(optarg); 644114402Sru if (n < 0) 645114402Sru error("unknown paper size `%1'", optarg); 646114402Sru else 647114402Sru user_paper_size = n; 648114402Sru break; 649114402Sru } 650114402Sru case 'v': 651151497Sru printf("GNU grolj4 (groff) version %s\n", Version_string); 652151497Sru exit(0); 653151497Sru break; 654114402Sru case 'F': 655114402Sru font::command_line_font_dir(optarg); 656114402Sru break; 657114402Sru case 'c': 658114402Sru { 659114402Sru char *ptr; 660114402Sru long n = strtol(optarg, &ptr, 10); 661114402Sru if (n == 0 && ptr == optarg) 662114402Sru error("argument for -c must be a positive integer"); 663114402Sru else if (n <= 0 || n > 32767) 664114402Sru error("out of range argument for -c"); 665114402Sru else 666114402Sru ncopies = unsigned(n); 667114402Sru break; 668114402Sru } 669114402Sru case 'w': 670114402Sru { 671114402Sru char *ptr; 672114402Sru long n = strtol(optarg, &ptr, 10); 673114402Sru if (n == 0 && ptr == optarg) 674114402Sru error("argument for -w must be a non-negative integer"); 675114402Sru else if (n < 0 || n > INT_MAX) 676114402Sru error("out of range argument for -w"); 677114402Sru else 678114402Sru line_width_factor = int(n); 679114402Sru break; 680114402Sru } 681114402Sru case CHAR_MAX + 1: // --help 682114402Sru usage(stdout); 683114402Sru exit(0); 684114402Sru break; 685114402Sru case '?': 686114402Sru usage(stderr); 687114402Sru exit(1); 688114402Sru break; 689114402Sru default: 690114402Sru assert(0); 691114402Sru } 692114402Sru SET_BINARY(fileno(stdout)); 693114402Sru if (optind >= argc) 694114402Sru do_file("-"); 695114402Sru else { 696114402Sru for (int i = optind; i < argc; i++) 697114402Sru do_file(argv[i]); 698114402Sru } 699114402Sru return 0; 700114402Sru} 701114402Sru 702114402Srustatic void usage(FILE *stream) 703114402Sru{ 704114402Sru fprintf(stream, 705114402Sru "usage: %s [-lv] [-d [n]] [-c n] [-p paper_size]\n" 706114402Sru " [-w n] [-F dir] [files ...]\n", 707114402Sru program_name); 708114402Sru} 709