Commit efc4fcd5 efc4fcd5d14ff42b4159cbd80d60ba7eea7df772 by Sergey Poznyakoff

Added framework for compose mode.

1 parent 513b47a4
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:
......