/contrib/groff/src/devices/xditview/device.c

https://bitbucket.org/freebsd/freebsd-head/ · C · 565 lines · 479 code · 67 blank · 19 comment · 134 complexity · e4c649fae8fb48595016917c9940c857 MD5 · raw file

  1. /* device.c */
  2. #ifdef HAVE_CONFIG_H
  3. #include <config.h>
  4. #endif
  5. #include <stdio.h>
  6. #include <ctype.h>
  7. #include <stdlib.h>
  8. #include <string.h>
  9. #include <X11/Xos.h>
  10. #include <X11/Intrinsic.h>
  11. #include "device.h"
  12. #include "defs.h"
  13. #ifndef isascii
  14. #define isascii(c) (1)
  15. #endif
  16. /* Name of environment variable containing path to be used for
  17. searching for device and font description files. */
  18. #define FONTPATH_ENV_VAR "GROFF_FONT_PATH"
  19. #define WS " \t\r\n"
  20. #ifndef INT_MIN
  21. /* Minimum and maximum values a `signed int' can hold. */
  22. #define INT_MIN (-INT_MAX-1)
  23. #define INT_MAX 2147483647
  24. #endif
  25. #define CHAR_TABLE_SIZE 307
  26. struct _DeviceFont {
  27. char *name;
  28. int special;
  29. DeviceFont *next;
  30. Device *dev;
  31. struct charinfo *char_table[CHAR_TABLE_SIZE];
  32. struct charinfo *code_table[256];
  33. };
  34. struct charinfo {
  35. int width;
  36. int code;
  37. struct charinfo *next;
  38. struct charinfo *code_next;
  39. char name[1];
  40. };
  41. static char *current_filename = 0;
  42. static int current_lineno = -1;
  43. static void error(const char *s);
  44. static FILE *open_device_file(const char *, const char *, char **);
  45. static DeviceFont *load_font(Device *, const char *);
  46. static Device *new_device(const char *);
  47. static DeviceFont *new_font(const char *, Device *);
  48. static void delete_font(DeviceFont *);
  49. static unsigned hash_name(const char *);
  50. static struct charinfo *add_char(DeviceFont *, const char *, int, int);
  51. static int read_charset_section(DeviceFont *, FILE *);
  52. static char *canonicalize_name(const char *);
  53. static int scale_round(int, int, int);
  54. static
  55. Device *new_device(const char *name)
  56. {
  57. Device *dev;
  58. dev = XtNew(Device);
  59. dev->sizescale = 1;
  60. dev->res = 0;
  61. dev->unitwidth = 0;
  62. dev->fonts = 0;
  63. dev->X11 = 0;
  64. dev->paperlength = 0;
  65. dev->paperwidth = 0;
  66. dev->name = XtNewString(name);
  67. return dev;
  68. }
  69. void device_destroy(Device *dev)
  70. {
  71. DeviceFont *f;
  72. if (!dev)
  73. return;
  74. f = dev->fonts;
  75. while (f) {
  76. DeviceFont *tem = f;
  77. f = f->next;
  78. delete_font(tem);
  79. }
  80. XtFree(dev->name);
  81. XtFree((char *)dev);
  82. }
  83. Device *device_load(const char *name)
  84. {
  85. Device *dev;
  86. FILE *fp;
  87. int err = 0;
  88. char buf[256];
  89. fp = open_device_file(name, "DESC", &current_filename);
  90. if (!fp)
  91. return 0;
  92. dev = new_device(name);
  93. current_lineno = 0;
  94. while (fgets(buf, sizeof(buf), fp)) {
  95. char *p;
  96. current_lineno++;
  97. p = strtok(buf, WS);
  98. if (p) {
  99. int *np = 0;
  100. char *q;
  101. if (strcmp(p, "charset") == 0)
  102. break;
  103. if (strcmp(p, "X11") == 0)
  104. dev->X11 = 1;
  105. else if (strcmp(p, "sizescale") == 0)
  106. np = &dev->sizescale;
  107. else if (strcmp(p, "res") == 0)
  108. np = &dev->res;
  109. else if (strcmp(p, "unitwidth") == 0)
  110. np = &dev->unitwidth;
  111. else if (strcmp(p, "paperwidth") == 0)
  112. np = &dev->paperwidth;
  113. else if (strcmp(p, "paperlength") == 0)
  114. np = &dev->paperlength;
  115. if (np) {
  116. q = strtok((char *)0, WS);
  117. if (!q || sscanf(q, "%d", np) != 1 || *np <= 0) {
  118. error("bad argument");
  119. err = 1;
  120. break;
  121. }
  122. }
  123. }
  124. }
  125. fclose(fp);
  126. current_lineno = -1;
  127. if (!err) {
  128. if (dev->res == 0) {
  129. error("missing res line");
  130. err = 1;
  131. }
  132. else if (dev->unitwidth == 0) {
  133. error("missing unitwidth line");
  134. err = 1;
  135. }
  136. }
  137. if (dev->paperlength == 0)
  138. dev->paperlength = dev->res*11;
  139. if (dev->paperwidth == 0)
  140. dev->paperwidth = dev->res*8 + dev->res/2;
  141. if (err) {
  142. device_destroy(dev);
  143. dev = 0;
  144. }
  145. XtFree(current_filename);
  146. current_filename = 0;
  147. return dev;
  148. }
  149. DeviceFont *device_find_font(Device *dev, const char *name)
  150. {
  151. DeviceFont *f;
  152. if (!dev)
  153. return 0;
  154. for (f = dev->fonts; f; f = f->next)
  155. if (strcmp(f->name, name) == 0)
  156. return f;
  157. return load_font(dev, name);
  158. }
  159. static
  160. DeviceFont *load_font(Device *dev, const char *name)
  161. {
  162. FILE *fp;
  163. char buf[256];
  164. DeviceFont *f;
  165. int special = 0;
  166. fp = open_device_file(dev->name, name, &current_filename);
  167. if (!fp)
  168. return 0;
  169. current_lineno = 0;
  170. for (;;) {
  171. char *p;
  172. if (!fgets(buf, sizeof(buf), fp)) {
  173. error("no charset line");
  174. return 0;
  175. }
  176. current_lineno++;
  177. p = strtok(buf, WS);
  178. /* charset must be on a line by itself */
  179. if (p && strcmp(p, "charset") == 0 && strtok((char *)0, WS) == 0)
  180. break;
  181. if (p && strcmp(p, "special") == 0)
  182. special = 1;
  183. }
  184. f = new_font(name, dev);
  185. f->special = special;
  186. if (!read_charset_section(f, fp)) {
  187. delete_font(f);
  188. f = 0;
  189. }
  190. else {
  191. f->next = dev->fonts;
  192. dev->fonts = f;
  193. }
  194. fclose(fp);
  195. XtFree(current_filename);
  196. current_filename = 0;
  197. return f;
  198. }
  199. static
  200. DeviceFont *new_font(const char *name, Device *dev)
  201. {
  202. int i;
  203. DeviceFont *f;
  204. f = XtNew(DeviceFont);
  205. f->name = XtNewString(name);
  206. f->dev = dev;
  207. f->special = 0;
  208. f->next = 0;
  209. for (i = 0; i < CHAR_TABLE_SIZE; i++)
  210. f->char_table[i] = 0;
  211. for (i = 0; i < 256; i++)
  212. f->code_table[i] = 0;
  213. return f;
  214. }
  215. static
  216. void delete_font(DeviceFont *f)
  217. {
  218. int i;
  219. if (!f)
  220. return;
  221. XtFree(f->name);
  222. for (i = 0; i < CHAR_TABLE_SIZE; i++) {
  223. struct charinfo *ptr = f->char_table[i];
  224. while (ptr) {
  225. struct charinfo *tem = ptr;
  226. ptr = ptr->next;
  227. XtFree((char *)tem);
  228. }
  229. }
  230. XtFree((char *)f);
  231. }
  232. static
  233. unsigned hash_name(const char *name)
  234. {
  235. unsigned n = 0;
  236. /* XXX do better than this */
  237. while (*name)
  238. n = (n << 1) ^ *name++;
  239. return n;
  240. }
  241. static
  242. int scale_round(int n, int x, int y)
  243. {
  244. int y2;
  245. if (x == 0)
  246. return 0;
  247. y2 = y/2;
  248. if (n >= 0) {
  249. if (n <= (INT_MAX - y2)/x)
  250. return (n*x + y2)/y;
  251. return (int)(n*(double)x/(double)y + .5);
  252. }
  253. else {
  254. if (-(unsigned)n <= (-(unsigned)INT_MIN - y2)/x)
  255. return (n*x - y2)/y;
  256. return (int)(n*(double)x/(double)y + .5);
  257. }
  258. }
  259. static
  260. char *canonicalize_name(const char *s)
  261. {
  262. static char ch[2];
  263. if (s[0] == 'c' && s[1] == 'h' && s[2] == 'a' && s[3] == 'r') {
  264. const char *p;
  265. int n;
  266. for (p = s + 4; *p; p++)
  267. if (!isascii(*p) || !isdigit((unsigned char)*p))
  268. return (char *)s;
  269. n = atoi(s + 4);
  270. if (n >= 0 && n <= 0xff) {
  271. ch[0] = (char)n;
  272. return ch;
  273. }
  274. }
  275. return (char *)s;
  276. }
  277. /* Return 1 if the character is present in the font; widthp gets the
  278. width if non-null. */
  279. int device_char_width(DeviceFont *f, int ps, const char *name, int *widthp)
  280. {
  281. struct charinfo *p;
  282. name = canonicalize_name(name);
  283. for (p = f->char_table[hash_name(name) % CHAR_TABLE_SIZE];; p = p->next) {
  284. if (!p)
  285. return 0;
  286. if (strcmp(p->name, name) == 0)
  287. break;
  288. }
  289. *widthp = scale_round(p->width, ps, f->dev->unitwidth);
  290. return 1;
  291. }
  292. int device_code_width(DeviceFont *f, int ps, int code, int *widthp)
  293. {
  294. struct charinfo *p;
  295. for (p = f->code_table[code & 0xff];; p = p->code_next) {
  296. if (!p)
  297. return 0;
  298. if (p->code == code)
  299. break;
  300. }
  301. *widthp = scale_round(p->width, ps, f->dev->unitwidth);
  302. return 1;
  303. }
  304. char *device_name_for_code(DeviceFont *f, int code)
  305. {
  306. static struct charinfo *state = 0;
  307. if (f)
  308. state = f->code_table[code & 0xff];
  309. for (; state; state = state->code_next)
  310. if (state->code == code && state->name[0] != '\0') {
  311. char *name = state->name;
  312. state = state->code_next;
  313. return name;
  314. }
  315. return 0;
  316. }
  317. int device_font_special(DeviceFont *f)
  318. {
  319. return f->special;
  320. }
  321. static
  322. struct charinfo *add_char(DeviceFont *f, const char *name, int width, int code)
  323. {
  324. struct charinfo **pp;
  325. struct charinfo *ci;
  326. name = canonicalize_name(name);
  327. if (strcmp(name, "---") == 0)
  328. name = "";
  329. ci = (struct charinfo *)XtMalloc(XtOffsetOf(struct charinfo, name[0])
  330. + strlen(name) + 1);
  331. strcpy(ci->name, name);
  332. ci->width = width;
  333. ci->code = code;
  334. if (*name != '\0') {
  335. pp = &f->char_table[hash_name(name) % CHAR_TABLE_SIZE];
  336. ci->next = *pp;
  337. *pp = ci;
  338. }
  339. pp = &f->code_table[code & 0xff];
  340. ci->code_next = *pp;
  341. *pp = ci;
  342. return ci;
  343. }
  344. /* Return non-zero for success. */
  345. static
  346. int read_charset_section(DeviceFont *f, FILE *fp)
  347. {
  348. struct charinfo *last_charinfo = 0;
  349. char buf[256];
  350. while (fgets(buf, sizeof(buf), fp)) {
  351. char *name;
  352. int width;
  353. int code;
  354. char *p;
  355. current_lineno++;
  356. name = strtok(buf, WS);
  357. if (!name)
  358. continue; /* ignore blank lines */
  359. p = strtok((char *)0, WS);
  360. if (!p) /* end of charset section */
  361. break;
  362. if (strcmp(p, "\"") == 0) {
  363. if (!last_charinfo) {
  364. error("first line of charset section cannot use `\"'");
  365. return 0;
  366. }
  367. else
  368. (void)add_char(f, name,
  369. last_charinfo->width, last_charinfo->code);
  370. }
  371. else {
  372. char *q;
  373. if (sscanf(p, "%d", &width) != 1) {
  374. error("bad width field");
  375. return 0;
  376. }
  377. p = strtok((char *)0, WS);
  378. if (!p) {
  379. error("missing type field");
  380. return 0;
  381. }
  382. p = strtok((char *)0, WS);
  383. if (!p) {
  384. error("missing code field");
  385. return 0;
  386. }
  387. code = (int)strtol(p, &q, 0);
  388. if (q == p) {
  389. error("bad code field");
  390. return 0;
  391. }
  392. last_charinfo = add_char(f, name, width, code);
  393. }
  394. }
  395. return 1;
  396. }
  397. static
  398. FILE *find_file(const char *file, char **result)
  399. {
  400. char *buf = NULL;
  401. int bufsiz = 0;
  402. int flen;
  403. FILE *fp;
  404. char *path;
  405. char *env;
  406. env = getenv(FONTPATH_ENV_VAR);
  407. path = XtMalloc(((env && *env) ? strlen(env) + 1 : 0)
  408. + strlen(FONTPATH) + 1);
  409. *path = '\0';
  410. if (env && *env) {
  411. strcat(path, env);
  412. strcat(path, ":");
  413. }
  414. strcat(path, FONTPATH);
  415. *result = NULL;
  416. if (file == NULL)
  417. return NULL;
  418. if (*file == '\0')
  419. return NULL;
  420. if (*file == '/') {
  421. fp = fopen(file, "r");
  422. if (fp)
  423. *result = XtNewString(file);
  424. return fp;
  425. }
  426. flen = strlen(file);
  427. while (*path) {
  428. int len;
  429. char *start, *end;
  430. start = path;
  431. end = strchr(path, ':');
  432. if (end)
  433. path = end + 1;
  434. else
  435. path = end = strchr(path, '\0');
  436. if (start >= end)
  437. continue;
  438. if (end[-1] == '/')
  439. --end;
  440. len = (end - start) + 1 + flen + 1;
  441. if (len > bufsiz) {
  442. if (buf)
  443. buf = XtRealloc(buf, len);
  444. else
  445. buf = XtMalloc(len);
  446. bufsiz = len;
  447. }
  448. memcpy(buf, start, end - start);
  449. buf[end - start] = '/';
  450. strcpy(buf + (end - start) + 1, file);
  451. fp = fopen(buf, "r");
  452. if (fp) {
  453. *result = buf;
  454. return fp;
  455. }
  456. }
  457. XtFree(buf);
  458. return NULL;
  459. }
  460. static
  461. FILE *open_device_file(const char *device_name, const char *file_name,
  462. char **result)
  463. {
  464. char *buf;
  465. FILE *fp;
  466. buf = XtMalloc(3 + strlen(device_name) + 1 + strlen(file_name) + 1);
  467. sprintf(buf, "dev%s/%s", device_name, file_name);
  468. fp = find_file(buf, result);
  469. if (!fp) {
  470. fprintf(stderr, "can't find device file `%s'\n", file_name);
  471. fflush(stderr);
  472. }
  473. XtFree(buf);
  474. return fp;
  475. }
  476. static
  477. void error(const char *s)
  478. {
  479. if (current_filename) {
  480. fprintf(stderr, "%s:", current_filename);
  481. if (current_lineno > 0)
  482. fprintf(stderr, "%d:", current_lineno);
  483. putc(' ', stderr);
  484. }
  485. fputs(s, stderr);
  486. putc('\n', stderr);
  487. fflush(stderr);
  488. }
  489. /*
  490. Local Variables:
  491. c-indent-level: 4
  492. c-continued-statement-offset: 4
  493. c-brace-offset: -4
  494. c-argdecl-indent: 4
  495. c-label-offset: -4
  496. c-tab-always-indent: nil
  497. End:
  498. */