Added framework for compose mode.
Showing
1 changed file
with
444 additions
and
2 deletions
... | @@ -18,6 +18,7 @@ | ... | @@ -18,6 +18,7 @@ |
18 | /* MH mhn command */ | 18 | /* MH mhn command */ |
19 | 19 | ||
20 | #include <mh.h> | 20 | #include <mh.h> |
21 | #include <mailutils/mime.h> | ||
21 | #define obstack_chunk_alloc xmalloc | 22 | #define obstack_chunk_alloc xmalloc |
22 | #define obstack_chunk_free free | 23 | #define obstack_chunk_free free |
23 | #include <obstack.h> | 24 | #include <obstack.h> |
... | @@ -1483,6 +1484,433 @@ mhn_store () | ... | @@ -1483,6 +1484,433 @@ mhn_store () |
1483 | } | 1484 | } |
1484 | 1485 | ||
1485 | 1486 | ||
1487 | /* ***************************** Compose Mode **************************** */ | ||
1488 | |||
1489 | int | ||
1490 | stream_getline (stream_t str, char **buf, size_t *bufsize, size_t *pnum) | ||
1491 | { | ||
1492 | int rc; | ||
1493 | size_t numread, n; | ||
1494 | |||
1495 | if (!*buf) | ||
1496 | { | ||
1497 | *bufsize = 128; | ||
1498 | *buf = xmalloc (*bufsize); | ||
1499 | } | ||
1500 | numread = 0; | ||
1501 | while ((rc = stream_sequential_readline (str, *buf + numread, | ||
1502 | *bufsize, &n)) == 0 | ||
1503 | && n > 0) | ||
1504 | { | ||
1505 | numread += n; | ||
1506 | if ((*buf)[numread - 1] != '\n') | ||
1507 | { | ||
1508 | if (numread == *bufsize) | ||
1509 | { | ||
1510 | *bufsize += 128; | ||
1511 | *buf = xrealloc (*buf, *bufsize); | ||
1512 | } | ||
1513 | continue; | ||
1514 | } | ||
1515 | break; | ||
1516 | } | ||
1517 | if (pnum) | ||
1518 | *pnum = numread; | ||
1519 | return rc; | ||
1520 | } | ||
1521 | |||
1522 | struct compose_env { | ||
1523 | stream_t input; | ||
1524 | mime_t mime; | ||
1525 | size_t line; | ||
1526 | }; | ||
1527 | |||
1528 | size_t | ||
1529 | mhn_error_loc (struct compose_env *env) | ||
1530 | { | ||
1531 | header_t hdr = NULL; | ||
1532 | size_t n = 0; | ||
1533 | message_get_header (message, &hdr); | ||
1534 | header_lines (hdr, &n); | ||
1535 | return n + 1 + env->line; | ||
1536 | } | ||
1537 | |||
1538 | static int | ||
1539 | parse_brace (char **pval, char **cmd, int c, struct compose_env *env) | ||
1540 | { | ||
1541 | char *val; | ||
1542 | int len; | ||
1543 | char *rest = *cmd; | ||
1544 | char *sp = strchr (rest, c); | ||
1545 | |||
1546 | if (!sp) | ||
1547 | { | ||
1548 | mh_error ("%s:%lu: missing %c", | ||
1549 | input_file, | ||
1550 | (unsigned long) mhn_error_loc (env), | ||
1551 | c); | ||
1552 | return 1; | ||
1553 | } | ||
1554 | len = sp - rest; | ||
1555 | val = xmalloc (len + 1); | ||
1556 | memcpy (val, rest, len); | ||
1557 | val[len] = 0; | ||
1558 | *cmd = sp + 1; | ||
1559 | *pval = val; | ||
1560 | return 0; | ||
1561 | } | ||
1562 | |||
1563 | /* cmd is: type "/" subtype | ||
1564 | 0*(";" attribute "=" value) | ||
1565 | [ "(" comment ")" ] | ||
1566 | [ "<" id ">" ] | ||
1567 | [ "[" description "]" ] | ||
1568 | */ | ||
1569 | int | ||
1570 | parse_type_command (char *cmd, char **prest, struct compose_env *env, | ||
1571 | header_t hdr) | ||
1572 | { | ||
1573 | int status = 0, stop = 0; | ||
1574 | char *sp; | ||
1575 | char *type = NULL; | ||
1576 | char *subtype = NULL; | ||
1577 | char *comment = NULL, *descr = NULL, *id = NULL; | ||
1578 | struct obstack stk; | ||
1579 | char *rest = *prest; | ||
1580 | |||
1581 | while (*rest && isspace (*rest)) | ||
1582 | rest++; | ||
1583 | split_content (cmd, &type, &subtype); | ||
1584 | if (!subtype) | ||
1585 | { | ||
1586 | if (*rest != '/') | ||
1587 | { | ||
1588 | mh_error ("%s:%lu: expected / but found %s", | ||
1589 | input_file, | ||
1590 | (unsigned long) mhn_error_loc (env), | ||
1591 | rest); | ||
1592 | return 1; | ||
1593 | } | ||
1594 | for (rest++; *rest && isspace (*rest); rest++) | ||
1595 | ; | ||
1596 | if (!*rest) | ||
1597 | { | ||
1598 | mh_error ("%s:%lu: missing subtype", | ||
1599 | input_file, | ||
1600 | (unsigned long) mhn_error_loc (env)); | ||
1601 | return 1; | ||
1602 | } | ||
1603 | subtype = strtok_r (rest, ";\n", &sp); | ||
1604 | subtype = strdup (subtype); | ||
1605 | rest = sp; | ||
1606 | } | ||
1607 | |||
1608 | obstack_init (&stk); | ||
1609 | obstack_grow (&stk, type, strlen (type)); | ||
1610 | obstack_1grow (&stk, '/'); | ||
1611 | obstack_grow (&stk, subtype, strlen (subtype)); | ||
1612 | |||
1613 | while (stop == 0 && status == 0 && *rest) | ||
1614 | { | ||
1615 | switch (*rest++) | ||
1616 | { | ||
1617 | case '(': | ||
1618 | if (comment) | ||
1619 | { | ||
1620 | mh_error ("%s:%lu: comment redefined", | ||
1621 | input_file, | ||
1622 | (unsigned long) mhn_error_loc (env)); | ||
1623 | status = 1; | ||
1624 | break; | ||
1625 | } | ||
1626 | status = parse_brace (&comment, &rest, ')', env); | ||
1627 | break; | ||
1628 | |||
1629 | case '[': | ||
1630 | if (descr) | ||
1631 | { | ||
1632 | mh_error ("%s:%lu: description redefined", | ||
1633 | input_file, | ||
1634 | (unsigned long) mhn_error_loc (env)); | ||
1635 | status = 1; | ||
1636 | break; | ||
1637 | } | ||
1638 | status = parse_brace (&descr, &rest, ']', env); | ||
1639 | break; | ||
1640 | |||
1641 | case '<': | ||
1642 | if (id) | ||
1643 | { | ||
1644 | mh_error ("%s:%lu: content id redefined", | ||
1645 | input_file, | ||
1646 | (unsigned long) mhn_error_loc (env)); | ||
1647 | status = 1; | ||
1648 | break; | ||
1649 | } | ||
1650 | status = parse_brace (&id, &rest, '>', env); | ||
1651 | break; | ||
1652 | |||
1653 | case ';': | ||
1654 | obstack_1grow (&stk, ';'); | ||
1655 | while (*rest && isspace (*rest)) | ||
1656 | rest++; | ||
1657 | sp = rest; | ||
1658 | for (; *rest && !isspace (*rest); rest++) | ||
1659 | obstack_1grow (&stk, *rest); | ||
1660 | while (*rest && isspace (*rest)) | ||
1661 | rest++; | ||
1662 | if (*rest != '=') | ||
1663 | { | ||
1664 | mh_error ("%s:%lu: syntax error", | ||
1665 | input_file, | ||
1666 | (unsigned long) mhn_error_loc (env)); | ||
1667 | status = 1; | ||
1668 | break; | ||
1669 | } | ||
1670 | obstack_1grow (&stk, '='); | ||
1671 | while (*rest && isspace (*rest)) | ||
1672 | rest++; | ||
1673 | for (; *rest; rest++) | ||
1674 | { | ||
1675 | if (strchr (";<[(", *rest)) | ||
1676 | break; | ||
1677 | obstack_1grow (&stk, *rest); | ||
1678 | } | ||
1679 | break; | ||
1680 | |||
1681 | default: | ||
1682 | stop = 1; | ||
1683 | break; | ||
1684 | } | ||
1685 | } | ||
1686 | |||
1687 | obstack_1grow (&stk, 0); | ||
1688 | header_set_value (hdr, MU_HEADER_CONTENT_TYPE, obstack_finish (&stk), 1); | ||
1689 | obstack_free (&stk, NULL); | ||
1690 | |||
1691 | if (!id) | ||
1692 | id = mh_create_message_id (1); | ||
1693 | |||
1694 | header_set_value (hdr, MU_HEADER_CONTENT_ID, id, 1); | ||
1695 | |||
1696 | free (comment); /* FIXME: comment was not used */ | ||
1697 | free (descr); | ||
1698 | free (id); | ||
1699 | *prest = rest; | ||
1700 | return status; | ||
1701 | } | ||
1702 | |||
1703 | int | ||
1704 | edit_extern (char *cmd, char *rest, struct compose_env *env, int level) | ||
1705 | { | ||
1706 | return 0; | ||
1707 | } | ||
1708 | |||
1709 | int | ||
1710 | edit_forw (char *cmd, struct compose_env *env, int level) | ||
1711 | { | ||
1712 | return 0; | ||
1713 | } | ||
1714 | |||
1715 | int | ||
1716 | edit_mime (char *cmd, char *rest, struct compose_env *env, int level) | ||
1717 | { | ||
1718 | int rc; | ||
1719 | message_t msg; | ||
1720 | header_t hdr; | ||
1721 | |||
1722 | message_create (&msg, NULL); | ||
1723 | message_get_header (msg, &hdr); | ||
1724 | rc = parse_type_command (cmd, &rest, env, hdr); | ||
1725 | if (rc == 0) | ||
1726 | mime_add_part (env->mime, msg); | ||
1727 | message_unref (msg); | ||
1728 | return rc; | ||
1729 | } | ||
1730 | |||
1731 | int | ||
1732 | mhn_edit (struct compose_env *env, int level) | ||
1733 | { | ||
1734 | int status = 0; | ||
1735 | char *buf = NULL; | ||
1736 | size_t bufsize = 0, n; | ||
1737 | body_t body; | ||
1738 | stream_t output; | ||
1739 | message_t msg = NULL; | ||
1740 | |||
1741 | while (status == 0 | ||
1742 | && stream_getline (env->input, &buf, &bufsize, &n) == 0 && n > 0) | ||
1743 | { | ||
1744 | env->line++; | ||
1745 | if (!msg) | ||
1746 | { | ||
1747 | /* Create new message */ | ||
1748 | message_create (&msg, NULL); | ||
1749 | /*FIXME: Headers*/ | ||
1750 | message_get_body (msg, &body); | ||
1751 | body_get_stream (body, &output); | ||
1752 | stream_seek (output, 0, SEEK_SET); | ||
1753 | } | ||
1754 | |||
1755 | if (buf[0] == '#') | ||
1756 | { | ||
1757 | if (buf[1] == '#') | ||
1758 | stream_sequential_write (output, buf+1, n-1); | ||
1759 | else | ||
1760 | { | ||
1761 | char *b2 = NULL; | ||
1762 | size_t bs = 0, n2; | ||
1763 | char *tok, *sp; | ||
1764 | |||
1765 | /* Collect the whole line */ | ||
1766 | while (n > 2 && buf[n-2] == '\\') | ||
1767 | { | ||
1768 | int rc = stream_getline (env->input, &b2, &bs, &n2); | ||
1769 | env->line++; | ||
1770 | if (rc == 0 && n2 > 0) | ||
1771 | { | ||
1772 | if (n + n2 > bufsize) | ||
1773 | { | ||
1774 | bufsize += 128; | ||
1775 | buf = xrealloc (buf, bufsize); | ||
1776 | } | ||
1777 | memcpy (buf + n - 2, b2, n2); | ||
1778 | n += n2 - 2; | ||
1779 | } | ||
1780 | } | ||
1781 | free (b2); | ||
1782 | |||
1783 | /* Close and append the previous part */ | ||
1784 | stream_close (output); | ||
1785 | mime_add_part (env->mime, msg); | ||
1786 | message_unref (msg); | ||
1787 | msg = NULL; | ||
1788 | |||
1789 | /* Execute the directive */ | ||
1790 | tok = strtok_r (buf, " \n", &sp); | ||
1791 | if (tok[1] == '@') | ||
1792 | status = edit_extern (tok + 2, sp, env, level); | ||
1793 | else if (strcmp (tok, "#forw") == 0) | ||
1794 | status = edit_forw (sp, env, level); | ||
1795 | else if (strcmp (tok, "#begin") == 0) | ||
1796 | { | ||
1797 | struct compose_env new_env; | ||
1798 | message_t new_msg; | ||
1799 | |||
1800 | new_env.input = env->input; | ||
1801 | new_env.line = env->line; | ||
1802 | mime_create (&new_env.mime, NULL, 0); | ||
1803 | status = mhn_edit (&new_env, level + 1); | ||
1804 | env->line = new_env.line; | ||
1805 | if (status == 0) | ||
1806 | { | ||
1807 | mime_get_message (new_env.mime, &new_msg); | ||
1808 | mime_add_part (env->mime, new_msg); | ||
1809 | message_unref (new_msg); | ||
1810 | } | ||
1811 | } | ||
1812 | else if (strcmp (tok, "#end") == 0) | ||
1813 | { | ||
1814 | if (level == 0) | ||
1815 | { | ||
1816 | mh_error ("%s:%lu: unmatched #end", | ||
1817 | input_file, | ||
1818 | (unsigned long) mhn_error_loc (env)); | ||
1819 | status = 1; | ||
1820 | } | ||
1821 | break; | ||
1822 | } | ||
1823 | else | ||
1824 | status = edit_mime (tok + 1, sp, env, level); | ||
1825 | } | ||
1826 | } | ||
1827 | else | ||
1828 | stream_sequential_write (output, buf, n); | ||
1829 | } | ||
1830 | free (buf); | ||
1831 | |||
1832 | if (msg) | ||
1833 | { | ||
1834 | stream_close (output); | ||
1835 | mime_add_part (env->mime, msg); | ||
1836 | message_unref (msg); | ||
1837 | } | ||
1838 | |||
1839 | return status; | ||
1840 | } | ||
1841 | |||
1842 | void | ||
1843 | copy_header (message_t msg, stream_t stream) | ||
1844 | { | ||
1845 | header_t hdr; | ||
1846 | stream_t in; | ||
1847 | char *buf = NULL; | ||
1848 | size_t bufsize, n; | ||
1849 | |||
1850 | message_get_header (msg, &hdr); | ||
1851 | header_get_stream (hdr, &in); | ||
1852 | stream_seek (in, 0, SEEK_SET); | ||
1853 | while (stream_getline (in, &buf, &bufsize, &n) == 0 && n > 0) | ||
1854 | { | ||
1855 | if (n == 1 && buf[0] == '\n') | ||
1856 | break; | ||
1857 | stream_sequential_write (stream, buf, n); | ||
1858 | } | ||
1859 | free (buf); | ||
1860 | } | ||
1861 | |||
1862 | int | ||
1863 | mhn_compose () | ||
1864 | { | ||
1865 | int rc; | ||
1866 | mime_t mime = NULL; | ||
1867 | body_t body; | ||
1868 | stream_t stream, in; | ||
1869 | struct compose_env env; | ||
1870 | message_t msg; | ||
1871 | char *name; | ||
1872 | |||
1873 | mime_create (&mime, NULL, 0); | ||
1874 | |||
1875 | message_get_body (message, &body); | ||
1876 | body_get_stream (body, &stream); | ||
1877 | stream_seek (stream, 0, SEEK_SET); | ||
1878 | |||
1879 | env.mime = mime; | ||
1880 | env.input = stream; | ||
1881 | rc = mhn_edit (&env, 0); | ||
1882 | if (rc) | ||
1883 | return rc; | ||
1884 | |||
1885 | mime_get_message (mime, &msg); | ||
1886 | asprintf (&name, "%s/draft.mhn", mu_path_folder_dir); | ||
1887 | unlink (name); | ||
1888 | rc = file_stream_create (&stream, name, MU_STREAM_RDWR|MU_STREAM_CREAT); | ||
1889 | if (rc) | ||
1890 | { | ||
1891 | mh_error (_("can't create output stream (file %s): %s"), | ||
1892 | name, mu_strerror (rc)); | ||
1893 | free (name); | ||
1894 | return rc; | ||
1895 | } | ||
1896 | rc = stream_open (stream); | ||
1897 | if (rc) | ||
1898 | { | ||
1899 | mh_error (_("can't open output stream (file %s): %s"), | ||
1900 | name, mu_strerror (rc)); | ||
1901 | free (name); | ||
1902 | stream_destroy (&stream, stream_get_owner (stream)); | ||
1903 | return rc; | ||
1904 | } | ||
1905 | free (name); | ||
1906 | |||
1907 | copy_header (message, stream); | ||
1908 | message_get_stream (msg, &in); | ||
1909 | cat_message (stream, in); | ||
1910 | stream_destroy (&stream, stream_get_owner (stream)); | ||
1911 | return 0; | ||
1912 | } | ||
1913 | |||
1486 | 1914 | ||
1487 | /* *************************** Main Entry Point ************************** */ | 1915 | /* *************************** Main Entry Point ************************** */ |
1488 | 1916 | ||
... | @@ -1511,6 +1939,18 @@ main (int argc, char **argv) | ... | @@ -1511,6 +1939,18 @@ main (int argc, char **argv) |
1511 | if (!message) | 1939 | if (!message) |
1512 | return 1; | 1940 | return 1; |
1513 | } | 1941 | } |
1942 | else if (mode == mode_compose) | ||
1943 | { | ||
1944 | if (argc > 1) | ||
1945 | { | ||
1946 | mh_error (_("extra arguments")); | ||
1947 | return 1; | ||
1948 | } | ||
1949 | input_file = argc == 1 ? argv[0] : "draft"; | ||
1950 | message = mh_file_to_message (mu_path_folder_dir, input_file); | ||
1951 | if (!message) | ||
1952 | return 1; | ||
1953 | } | ||
1514 | else | 1954 | else |
1515 | { | 1955 | { |
1516 | mbox = mh_open_folder (current_folder, 0); | 1956 | mbox = mh_open_folder (current_folder, 0); |
... | @@ -1520,8 +1960,10 @@ main (int argc, char **argv) | ... | @@ -1520,8 +1960,10 @@ main (int argc, char **argv) |
1520 | switch (mode) | 1960 | switch (mode) |
1521 | { | 1961 | { |
1522 | case mode_compose: | 1962 | case mode_compose: |
1523 | mh_error ("mode is not yet implemented"); | 1963 | /* Prepare filename for diagnostic purposes */ |
1524 | rc = 1; | 1964 | if (input_file[0] != '/') |
1965 | asprintf (&input_file, "%s/%s", mu_path_folder_dir, input_file); | ||
1966 | rc = mhn_compose (); | ||
1525 | break; | 1967 | break; |
1526 | 1968 | ||
1527 | case mode_list: | 1969 | case mode_list: | ... | ... |
-
Please register or sign in to post a comment