Fedora 19 {though I doubt that's relevant]
If I invoke the info command for a topic that doesn't actually have an info node, but does have a man page, info will apparently create a node named (*manpages*)<topic> from the man page -- on the fly.
I can neither find this feature documented anywhere, nor (obviously) a description of how it's done.
Can anyone point me to some documentation about this?
I have checked the GNU standalone info manual and GNU Info manual and found nothing but a small sign that this feature exists. In the description of option --all we can read this:
--all
-a
Find all files matching the given menu-item (a file or node name).
Three usage patterns are supported, as follows.
First, if --all is used together with --where, info prints the names
of all matching files found on standard output (including *manpages*
if relevant) and exits.
So I'm afraid the only documentation is the source code. In info.c you can find the following function. Look for the "Fall back to loading man page." comment.
/* Get the initial Info file, either by following menus from "(dir)Top",
or what the user specifed with values in filename. */
static char *
get_initial_file (char *filename, int *argc, char ***argv, char **error)
{
char *initial_file = 0; /* First file loaded by Info. */
REFERENCE *entry;
/* If there are any more arguments, the initial file is the
dir entry given by the first one. */
if (!filename && (*argv)[0])
{
/* If they say info -O info, we want to show them the invocation node
for standalone info; there's nothing useful in info.texi. */
if (goto_invocation_p && (*argv)[0]
&& mbscasecmp ((*argv)[0], "info") == 0)
(*argv)[0] = "info-stnd";
entry = lookup_dir_entry ((*argv)[0], 0);
if (entry)
{
initial_file = info_find_fullpath (entry->filename, 0);
if (initial_file)
{
(*argv)++; /* Advance past first remaining argument. */
(*argc)--;
/* Store full path, so that we find the already loaded file in
info_find_file, and show the full path if --where is used. */
entry->filename = initial_file;
add_pointer_to_array (info_copy_reference (entry),
ref_index, ref_list, ref_slots, 2);
return initial_file;
}
}
}
/* User used "--file". */
if (filename)
{
initial_file = info_find_fullpath (filename, 0);
if (!initial_file)
{
if (filesys_error_number)
*error = filesys_error_string (filename, filesys_error_number);
}
else
return initial_file;
}
/* File name lookup. */
if (!filename && (*argv)[0])
{
/* Try finding a file with this name, in case
it exists, but wasn't listed in dir. */
initial_file = info_find_fullpath ((*argv)[0], 0);
if (initial_file)
{
(*argv)++; /* Advance past first remaining argument. */
(*argc)--;
return initial_file;
}
else
asprintf (error, _("No menu item `%s' in node `%s'."),
(*argv)[0], "(dir)Top");
}
/* Fall back to loading man page. */
if (filename || (*argv)[0])
{
NODE *man_node;
debug (3, ("falling back to manpage node"));
man_node = get_manpage_node (filename ? filename : (*argv)[0]);
if (man_node)
{
add_pointer_to_array
(info_new_reference (MANPAGE_FILE_BUFFER_NAME,
filename ? filename : (*argv)[0]),
ref_index, ref_list, ref_slots, 2);
initial_file = MANPAGE_FILE_BUFFER_NAME;
return initial_file;
}
}
/* Inexact dir lookup. */
if (!filename && (*argv)[0])
{
entry = lookup_dir_entry ((*argv)[0], 1);
if (entry)
{
(*argv)++; /* Advance past first remaining argument. */
(*argc)--;
/* Clear error message. */
free (*error);
*error = 0;
initial_file = info_find_fullpath (entry->filename, 0);
/* Store full path, so that we find the already loaded file in
info_find_file, and show the full path if --where is used. */
entry->filename = initial_file;
add_pointer_to_array (info_copy_reference (entry),
ref_index, ref_list, ref_slots, 2);
return initial_file;
}
}
/* Otherwise, we want the dir node. The only node to be displayed
or output will be "Top". */
return 0;
}
man.h contains the definition of MANPAGE_FILE_BUFFER_NAME:
#define MANPAGE_FILE_BUFFER_NAME "*manpages*"
info is a command like man, you can test the command : info make but info Read documentation in Info format.
Related
I am trying to match the description part (line without a # symbol) of JS doc string. Here are a couple of exmaples of doc strings.
/**
* This is a doc string
* #param withArg description of withArg
* #param withArg2 description of withArg2
*/
/**
* This is an other doc string
* This is second line of description.
*/
This seems to work in regex editor:
/\*\S*(.*?)(#)/
see: https://regexr.com/4dfbn
But in javascript:
https://jsbin.com/qekisogula/1/edit?html,js,console
Any ideas on what is going on ?
Thanks a lot
UPDATE:
Expected output
Example 1:
This is an other doc string
Example 2:
This is an other doc string, This is second line of description.
I would actually take the approach of reading the file line by line and using some rudimentary parsing logic:
var lineReader = require('readline').createInterface({
input: require('fs').createReadStream('file.in')
});
var start = false;
lineReader.on('line', function (line) {
if (line.startsWith("/**") {
start = true;
}
else if (line.startsWith("*/")) {
start = false;
}
else if (start && !line.includes("#param")) {
console.log("doc string: ", line);
}
});
The logic here is that we use a boolean flag start to keep track of whether we are inside a comment with doc strings. Upon hitting /** the flag is turned on, and upon hitting */ the flag is turned off. If we encounter a line which does not contain #param, then we echo it to the console.
Is there any way to customize the Nav Bar or the Header to have a custom link?
The use-case is that I have a JIRA issue collector that is driven by javascript. I would like the user to provide feedback from the page they are having issues. However, any solution I can come up with so far takes the user away from the current page.
Example of what I have that takes the user away:
I currently have a Suitelet that is in one of the menus. That Suitelet invokes javascript but even then the user is taken away.
I have a workflow on the case record that calls some Javascript Javascript in one of the UI-based action's conditions is invoked. Similar to #1 but on the case record.
I'm thinking I'm going to need to create and public a chrome extension for my company's domain just to get a pervasive bit of javascript to run for all pages...seems like a sledgehammer.
I hope someone can prove me wrong, but as far as I am aware there is no way to natively inject Javascript or anything into the NetSuite header/navbar - they don't offer customisation to the header/navbar.
I've resorted to creating a Userscript that I load through the Violent Monkey extension for Chrome or Firefox.
Example Userscript Template
// ==UserScript==
// #name NetSuite Mods (Example)
// #namespace Violentmonkey Scripts
// #match *.netsuite.com/*
// #include *.netsuite.com/*
// #grant GM_addStyle
// #version 1.0
// #author Kane Shaw - https://stackoverflow.com/users/4561907/kane-shaw
// #description 6/11/2020, 6:25:20 PM
// ==/UserScript==
// Get access to some commonly used NLAPI functions without having to use "unsafeWindow.nlapi..." in our code
// You can add more of these if you need access to more of the functions contained on the NetSuite page
nlapiSetFieldText = unsafeWindow.nlapiSetFieldText;
nlapiSetFieldValue = unsafeWindow.nlapiSetFieldValue;
nlapiGetFieldText = unsafeWindow.nlapiGetFieldText;
nlapiGetFieldValue = unsafeWindow.nlapiGetFieldValue;
nlapiSearchRecord = unsafeWindow.nlapiSearchRecord;
nlobjSearchFilter = unsafeWindow.nlobjSearchFilter;
nlapiLookupField = unsafeWindow.nlapiLookupField;
nlapiLoadRecord = unsafeWindow.nlapiLoadRecord;
nlapiSubmitRecord = unsafeWindow.nlapiSubmitRecord;
GM_pageTransformations = {};
/**
* The entrypoint for our userscript
*/
function GM_main(jQuery) {
// We want to execute these on every NetSuite page
GM_pageTransformations.header();
GM_pageTransformations.browsertitle();
// Here we build a function name from the path (page being accessed on the NetSuite domain)
var path = location.pathname;
if(path.indexOf('.')>-1) path = path.substr(0,path.indexOf('.'));
path = toCamelCase(path,'/');
// Now we check if a page "GM_pageTransformations" function exists with a matching name
if(GM_pageTransformations[path]) {
console.log('Executing GM_pageTransformations for '+path);
GM_pageTransformations[path]();
} else {
console.log('No GM_pageTransformations for '+path);
}
}
/**
* Changes the header on all pages
*/
GM_pageTransformations['header'] = function() {
// For example, lets make the header background red
GM_addStyle('#ns_header, #ns_header * { background: red !important; }');
}
/**
* Provides useful browser/tab titles for each NetSuite page
*/
GM_pageTransformations['browsertitle'] = function() {
var title = jQuery('.uir-page-title-secondline').text().trim();
var title2 = jQuery('.uir-page-title-firstline').text().trim();
var title3 = jQuery('.ns-dashboard-detail-name').text().trim();
if(title != '') {
document.title = title+(title2 ? ': '+title2 : '')+(title3 ? ': '+title3 : '');
} else if(title2 != '') {
document.title = title2+(title3 ? ': '+title3 : '');
} else if(title3 != '') {
document.title = title3;
}
}
/**
* Changes app center card pages (dashboard pages)
*/
GM_pageTransformations['appCenterCard'] = function() {
// For example, lets make add a new heading text on all Dashboard pages
jQuery('#ns-dashboard-page').prepend('<h1>My New Dashboard Title</h1>');
}
/**
* Convert a given string into camelCase, or CamelCase
* #param {String} string - The input stirng
* #param {String} delimter - The delimiter that seperates the words in the input string (default " ")
* #param {Boolean} capitalizeFirstWord - Wheater or not to capitalize the first word (default false)
*/
function toCamelCase(string, delimiter, capitalizeFirstWord) {
if(!delimiter) delimiter = ' ';
var pieces = string.split(delimiter);
string = '';
for (var i=0; i<pieces.length; i++) {
if(pieces[i].length == 0) continue;
string += pieces[i].charAt(0).toUpperCase() + pieces[i].slice(1);
}
if(!capitalizeFirstWord) string= string.charAt(0).toLowerCase()+string.slice(1);
return string;
}
// ===============
// CREDIT FOR JQUERY INCLUSION CODE: Brock Adams # https://stackoverflow.com/a/12751531/4561907
/**
* Check if we already have a local copy of jQuery, or if we need to fetch it from a 3rd-party server
*/
if (typeof GM_info !== "undefined") {
console.log("Running with local copy of jQuery!");
GM_main(jQuery);
}
else {
console.log ("fetching jQuery from some 3rd-party server.");
add_jQuery(GM_main, "1.9.0");
}
/**
* Add the jQuery into our page for our userscript to use
*/
function add_jQuery(callbackFn, jqVersion) {
var jqVersion = jqVersion || "1.9.0";
var D = document;
var targ = D.getElementsByTagName ('head')[0] || D.body || D.documentElement;
var scriptNode = D.createElement ('script');
scriptNode.src = 'https://ajax.googleapis.com/ajax/libs/jquery/'
+ jqVersion
+ '/jquery.min.js'
;
scriptNode.addEventListener ("load", function () {
var scriptNode = D.createElement ("script");
scriptNode.textContent =
'var gm_jQuery = jQuery.noConflict (true);\n'
+ '(' + callbackFn.toString () + ')(gm_jQuery);'
;
targ.appendChild (scriptNode);
}, false);
targ.appendChild (scriptNode);
}
You can copy and paste that code as-is into a new Userscript and it will do the following:
Make Browser tabs/windows have useful titles (shows order numbers, customer names, vendor names etc - not just "Sales Order")
Change the header background to red (as an example)
Add a new heading to the top of all "Dashboard" pages that says "My New Dashboard Title" (as an example)
Let's say I have the following (example) code in combined.js:
/* jQuery, Moment.js, Bootstrap, etc. */
Child.prototype.doSchool = function(data) { // Bookmarked
var essay = data.essay || {};
if (essay) {
var spelling = checkSpelling(essay, EN_US_GRADE_7);
return spelling.grade();
}
}
/* Extensive and Turing-complete code base */
var burt = new Child();
if (burt.doSchool({essay: "i like trains"}) < .65) burt.comfort(); // Bookmarked
/* jQuery extensions, Fallout 4, etc. */
The file is bookmarked in Komodo Edit 9.3.x in the locations marked by // inline comments.
Any /* block comments */ indicate thousands of lines of code.
The source between the bookmarks exists in another file, school.inc.js. I want to know if there is an easy way to select all the text between the bookmarks, so that combined.js can be easily updated by pasting the contents of school.inc.js over it without having to use a combining utility.
There is no built in way to do this but you could possible do it by writing a Userscript.
You'll want to use the Komodo Editor SDK.
// This assumes you're running the Userscript starting at the first bookmark
var editor = require("ko/editor");
var startSelect;
var endSelect;
var done = false;
function selectBookmarkRegion(){
if(editor.bookmarkExists()) { // check if bookmark is set on current line
startSelect = { // save it's line start
line: editor.getLineNumber(),
ch: 0
};
} else {
alert("Start me on a line with a Bookmark");
}
editor.goLineDown();
while(!done){
if(editor.bookmarkExists())
{
endSelect = {
line: editor.getLineNumber(),
ch: editor.getLineSize()
};// Save line end
done = true;
}
editor.goLineDown();
// found a bug as I was writing this. Will be fixed in the next releases
if (editor.getLineNumber() + 1 == editor.lineCount())
{
done = true;
}
}
editor.setSelection(startSelect, endSelect); // Wrap the selection
}
selectBookmarkRegion();
I am trying to change unknown uid's and gid's on files that my rsync command would consider as source files before I execute my rsync command.
My rsync command includes an exclude file.
The reason that I need to do this is explained in my question here.
I have tried this find command:
find /cygdrive/f -uid 4294967295 -exec chown 544. '{}' + -o -gid 4294967295 -exec chown .197121 '{}' +
BUT, it does not handle the exclude file. By that I mean, the above find searches all of f drive for files matching the unknown uid/gid, then chowns them. My rsync looks at drive f and copies all of it except the files in exclude file. I do not want to chown any Win7 side files that rsync does not copy.
For instance, one of the ways Win7 protects some of its hidden/sys files is by setting their uid and gid to 4294967295 (eg c:\pagefil.sys and c:\hiberfil.sys). I have excluded both these files in the rsync exclude file AND I want to leave their Win7 side uid/gid alone. The find command would chown them.
I have also tried to parse an ls listing, which may work, but very slowly. Since I am only dealing with Win7 files I think an ls would be suitable for parsing.
Is there a better way to solve my problem before I work with the ls listing (or parse the find output) before chowning script?
Another, more precise way, but slow and requiring a more difficult script for me, would be to parse an rsync --dry-run ... listing to figure out which items need chowning.
EDIT 2015-12-13: Unfortunately the rsync --dry-run ... does not generate the warnings about impossible to set UID/GID 's during the dry run so that method is out.
BUT, I have found the source code for rsync and it looks to me that it would be pretty easy to modify it so that the UID/GID 's could be set to the UID and GID of the process running the rsync command if the bad UID/GID 's are found during a session.
Can anyone give me a summary of what tools I need to compile the rsync source code on a Win7 computer?
Here is rsync.c from the source code (search for 'impossible to set'):
int set_file_attrs(const char *fname, struct file_struct *file, stat_x *sxp,
const char *fnamecmp, int flags)
{
int updated = 0;
stat_x sx2;
int change_uid, change_gid;
mode_t new_mode = file->mode;
int inherit;
if (!sxp) {
if (dry_run)
return 1;
if (link_stat(fname, &sx2.st, 0) < 0) {
rsyserr(FERROR_XFER, errno, "stat %s failed",
full_fname(fname));
return 0;
}
init_stat_x(&sx2);
sxp = &sx2;
inherit = !preserve_perms;
} else
inherit = !preserve_perms && file->flags & FLAG_DIR_CREATED;
if (inherit && S_ISDIR(new_mode) && sxp->st.st_mode & S_ISGID) {
/* We just created this directory and its setgid
* bit is on, so make sure it stays on. */
new_mode |= S_ISGID;
}
if (daemon_chmod_modes && !S_ISLNK(new_mode))
new_mode = tweak_mode(new_mode, daemon_chmod_modes);
#ifdef SUPPORT_ACLS
if (preserve_acls && !S_ISLNK(file->mode) && !ACL_READY(*sxp))
get_acl(fname, sxp);
#endif
change_uid = am_root && uid_ndx && sxp->st.st_uid != (uid_t)F_OWNER(file);
change_gid = gid_ndx && !(file->flags & FLAG_SKIP_GROUP)
&& sxp->st.st_gid != (gid_t)F_GROUP(file);
#ifndef CAN_CHOWN_SYMLINK
if (S_ISLNK(sxp->st.st_mode)) {
;
} else
#endif
if (change_uid || change_gid) {
if (DEBUG_GTE(OWN, 1)) {
if (change_uid) {
rprintf(FINFO,
"set uid of %s from %u to %u\n",
fname, (unsigned)sxp->st.st_uid, F_OWNER(file));
}
if (change_gid) {
rprintf(FINFO,
"set gid of %s from %u to %u\n",
fname, (unsigned)sxp->st.st_gid, F_GROUP(file));
}
}
if (am_root >= 0) {
uid_t uid = change_uid ? (uid_t)F_OWNER(file) : sxp->st.st_uid;
gid_t gid = change_gid ? (gid_t)F_GROUP(file) : sxp->st.st_gid;
if (do_lchown(fname, uid, gid) != 0) {
/* We shouldn't have attempted to change uid
* or gid unless have the privilege. */
rsyserr(FERROR_XFER, errno, "%s %s failed",
change_uid ? "chown" : "chgrp",
full_fname(fname));
goto cleanup;
}
if (uid == (uid_t)-1 && sxp->st.st_uid != (uid_t)-1)
rprintf(FERROR_XFER, "uid 4294967295 (-1) is impossible to set on %s\n", full_fname(fname));
if (gid == (gid_t)-1 && sxp->st.st_gid != (gid_t)-1)
rprintf(FERROR_XFER, "gid 4294967295 (-1) is impossible to set on %s\n", full_fname(fname));
/* A lchown had been done, so we need to re-stat if
* the destination had the setuid or setgid bits set
* (due to the side effect of the chown call). */
if (sxp->st.st_mode & (S_ISUID | S_ISGID)) {
link_stat(fname, &sxp->st,
keep_dirlinks && S_ISDIR(sxp->st.st_mode));
}
}
updated = 1;
}
#ifdef SUPPORT_XATTRS
if (am_root < 0)
set_stat_xattr(fname, file, new_mode);
if (preserve_xattrs && fnamecmp)
set_xattr(fname, file, fnamecmp, sxp);
#endif
if (!preserve_times
|| (!(preserve_times & PRESERVE_DIR_TIMES) && S_ISDIR(sxp->st.st_mode))
|| (!(preserve_times & PRESERVE_LINK_TIMES) && S_ISLNK(sxp->st.st_mode)))
flags |= ATTRS_SKIP_MTIME;
if (!(flags & ATTRS_SKIP_MTIME)
&& cmp_time(sxp->st.st_mtime, file->modtime) != 0) {
int ret = set_modtime(fname, file->modtime, F_MOD_NSEC(file), sxp->st.st_mode);
if (ret < 0) {
rsyserr(FERROR_XFER, errno, "failed to set times on %s",
full_fname(fname));
goto cleanup;
}
if (ret == 0) /* ret == 1 if symlink could not be set */
updated = 1;
else
file->flags |= FLAG_TIME_FAILED;
}
#ifdef SUPPORT_ACLS
/* It's OK to call set_acl() now, even for a dir, as the generator
* will enable owner-writability using chmod, if necessary.
*
* If set_acl() changes permission bits in the process of setting
* an access ACL, it changes sxp->st.st_mode so we know whether we
* need to chmod(). */
if (preserve_acls && !S_ISLNK(new_mode)) {
if (set_acl(fname, file, sxp, new_mode) > 0)
updated = 1;
}
#endif
#ifdef HAVE_CHMOD
if (!BITS_EQUAL(sxp->st.st_mode, new_mode, CHMOD_BITS)) {
int ret = am_root < 0 ? 0 : do_chmod(fname, new_mode);
if (ret < 0) {
rsyserr(FERROR_XFER, errno,
"failed to set permissions on %s",
full_fname(fname));
goto cleanup;
}
if (ret == 0) /* ret == 1 if symlink could not be set */
updated = 1;
}
#endif
if (INFO_GTE(NAME, 2) && flags & ATTRS_REPORT) {
if (updated)
rprintf(FCLIENT, "%s\n", fname);
else
rprintf(FCLIENT, "%s is uptodate\n", fname);
}
cleanup:
if (sxp == &sx2)
free_stat_x(&sx2);
return updated;
}
I have found two practical solutions that fix the base issue:
If both source and destination environments use rsync 3.1.0 or newer, there are new options available. In that case I could just add these options to my rsync command:
--usermap=4294967295:544 --groupmap=4294967295:197121
Thank-you Wayne Davison for directing me to these new options!
If you are stuck with an older rsync on your destination (as I am with my WD MyCloud), you can modify the rsync source code with cygwin as follows.
Make sure your cygwin is installed with gcc gcc-core perl make and quilt
Download the latest rsync source tar file at rsync site
I unzipped that to a folder in my ~ directory.
I downloaded Eclipse to use as an IDE but you could just modify the files with NotePad++ as follows:
In the main.c file I added an information line that you see every time you run rsync so you know you are using your personal rsync version. I am sure that there is also an appropriate way to set the version number to one of my own but I will let someone comment on how to do that. (all my lines end with /* dalek */):
starttime = time(NULL);
our_uid = MY_UID();
our_gid = MY_GID();
rprintf(FINFO,"rsync 3.1.1 with edits started by uid: %u gid: %u\n", our_uid, our_gid ); /* dalek */
Then, in flist.c, add my /* dalek */ lines as follows:
if (!preserve_uid || ((uid_t)F_OWNER(file) == uid && *lastname))
xflags |= XMIT_SAME_UID;
else {
uid = F_OWNER(file);
if (uid==4294967295){ /* dalek */
if (do_lchown(fname, our_uid, F_GROUP(file)) != 0) { /* dalek */
rprintf(FINFO, "COULD NOT CHANGE 4294967295 UID to %u on %s\n",our_uid,fname); /* dalek */
}else{ /* dalek */
uid=our_uid; /* dalek */
} /* dalek */
} /* dalek */
if (!numeric_ids) {
user_name = add_uid(uid);
if (inc_recurse && user_name)
xflags |= XMIT_USER_NAME_FOLLOWS;
}
}
if (!preserve_gid || ((gid_t)F_GROUP(file) == gid && *lastname))
xflags |= XMIT_SAME_GID;
else {
gid = F_GROUP(file);
if (gid==4294967295){ /* dalek */
if (do_lchown(fname, F_OWNER(file), our_gid) != 0) { /* dalek */
rprintf(FINFO, "COULD NOT CHANGE 4294967295 GID to %u on %s\n",our_gid,fname); /* dalek */
}else{ /* dalek */
gid=our_gid; /* dalek */
} /* dalek */
} /* dalek */
if (!numeric_ids) {
group_name = add_gid(gid);
if (inc_recurse && group_name)
xflags |= XMIT_GROUP_NAME_FOLLOWS;
}
}
Then in your recently added rsync source directory run ./configure.sh then run make then run make install. That is it! You should have a new rsync.exe file in .../usr/local/bin which will be run whenever you use rsync from now on since cygwin puts .../usr/local/bin ahead of .../bin in the PATH it uses. The original rsync is still in .../bin. To use the original, just move your modified rsync.exe out of .../usr/local/bin.
If you have spare space on your Win7 computer, try this:
rsync the files you want into a temporary location on the same computer. Because it is the same computer the UID/GID should set successfully.
In the copy do your find/chown script to set the UID/GID for all the files.
rsync the copy back to the original location (carefully!) The contents of the files should not have changed, so the only changes rsync should make will be to set the UID/GID.
Make sure you use -aHAX to do the copies, and do a dry-run before overwriting anything!
I have been trying to append some text to outgoing mail from lotus notes client. For this I have registered EM_MAILSENDNOTE with EM_REG_BEFORE event. On this event, I am appending some rich text to mail body.
But that text appears only in the sent folder mail, not at the receiving end. What I am missing?
code below referred from this document.
void appendRichTextToBody(NOTEHANDLE note_handle, const char *szString1) {
#define PARA_STYLE_ID 1
char itemname[] = "Body";
WORD wBuffLen; /* required buffer length */
BYTE *rt_field; /* allocated rich-text field */
BYTE *buff_ptr; /* position in allocated memory */
CDPABDEFINITION pabdef; /* rich-text paragraph style */
CDPARAGRAPH para; /* rich-text paragraph header */
CDPABREFERENCE ref; /* rich-text style reference */
CDTEXT cdtext; /* rich-text text header */
WORD wString1Len = strlen( szString1 ); /* Length of actual string */
WORD wString1BufLen = wString1Len + (wString1Len%2);
FONTIDFIELDS *pFontID; /* font definitions in text header */
DWORD rt_size; /* size of rich-text field */
STATUS error = NOERROR; /* return code from API calls */
wBuffLen = ODSLength( _CDPABDEFINITION ) +
ODSLength( _CDPARAGRAPH ) + ODSLength( _CDPABREFERENCE) +
ODSLength(_CDTEXT) + wString1BufLen;
rt_field = (BYTE *) malloc ( wBuffLen );
if( rt_field == (BYTE *)NULL ) {
return;
}
//* Keep a pointer to our current position in the buffer. */
buff_ptr = rt_field;
/* Initialize a CDPABDEFINITION structure.We use all defaults, except for centered
justification.*/
memset(&pabdef, 0, sizeof(CDPABDEFINITION));
pabdef.Header.Signature = SIG_CD_PABDEFINITION;
pabdef.Header.Length = ODSLength( _CDPABDEFINITION );
pabdef.PABID = PARA_STYLE_ID;
pabdef.JustifyMode = JUSTIFY_CENTER;
pabdef.LineSpacing = DEFAULT_LINE_SPACING;
pabdef.ParagraphSpacingBefore = DEFAULT_ABOVE_PAR_SPACING;
pabdef.ParagraphSpacingAfter = DEFAULT_BELOW_PAR_SPACING;
pabdef.LeftMargin = DEFAULT_LEFT_MARGIN;
pabdef.RightMargin = DEFAULT_RIGHT_MARGIN;
pabdef.FirstLineLeftMargin = DEFAULT_FIRST_LEFT_MARGIN;
pabdef.Tabs = DEFAULT_TABS;
pabdef.Tab[0] = DEFAULT_TAB_INTERVAL;
pabdef.Flags = 0;
pabdef.TabTypes = TAB_DEFAULT;
pabdef.Flags2 = 0;
/* Call ODSWriteMemory to convert the CDPABDEFINITION structure to
Domino and Notes canonical format and write the converted structure into
the buffer at location buff_ptr. This advances buff_ptr to the
next byte in the buffer after the canonical format strucure.
*/
ODSWriteMemory( &buff_ptr, _CDPABDEFINITION, &pabdef, 1 );
/* Put a paragraph header in the field. */
para.Header.Signature = SIG_CD_PARAGRAPH;
para.Header.Length = (BYTE) ODSLength( _CDPARAGRAPH );
ODSWriteMemory( &buff_ptr, _CDPARAGRAPH, ¶, 1 );
/* Add the CDTEXT record to the field. A CDTEXT record consists
of a CDTEXT structure followed by a run of text. Initialize the
CDTEXT structure by filling in the signature and the length.
The CDTEXT structure also contains the font information that
controls how Domino and Notes displays this first run of text.
*/
cdtext.Header.Signature = SIG_CD_TEXT;
cdtext.Header.Length = ODSLength( _CDTEXT ) + wString1Len ;
pFontID = (FONTIDFIELDS *) &(cdtext.FontID);
pFontID->Face = FONT_FACE_SWISS;
pFontID->Attrib = ISBOLD;
pFontID->Color = NOTES_COLOR_BLUE;
pFontID->PointSize = 24;
ODSWriteMemory( &buff_ptr, _CDTEXT, &cdtext, 1 );
/* Write the actual characters of this first text run to the buffer.
Since the run of text may contian embedded null characters, use
memcpy, not strcpy. No need to terminate the run of text with a
null because the Header.Length member of the CDTEXT structure
specifies the length explicitly. Only copy the null if the
number of characters is odd.
*/
memcpy( (char *)buff_ptr, szString1, wString1BufLen );
/* Advance the pointer to the next even-byte boundary */
buff_ptr += wString1BufLen;
rt_size = (DWORD)(buff_ptr - rt_field);
if (error = NSFItemAppend( note_handle, 0, itemname, (WORD) strlen(itemname), TYPE_COMPOSITE, rt_field, rt_size ) ){
Log("%s: NSFItemAppend failed\n", __FUNCTION__);
return;
}
free( rt_field );
}
EDIT1:
Code to register event handler:
error = EMRegister(EM_MAILSENDNOTE, EM_REG_BEFORE,(EMHANDLER)MailSendNoteHandler, gRecursionID, &hMailSendNote);
error = EMRegister(EM_NSFNOTEUPDATEMAILBOX, EM_REG_BEFORE,(EMHANDLER)UpdateMailboxHandler, gRecursionID, &hNSFUpdateMailbox);
Event handler functions:
STATUS MailSendNoteHandler(EMRECORD FAR * pExRecord) {
VARARG_PTR ap = pExRecord->Ap;
DHANDLE hNote = VARARG_GET (ap, DHANDLE);
appendRichTextToBody(hNote, " Test Rich Text EM_MAILSENDNOTE BEFORE ");
return (EM_ERR_CONTINUE);
}
STATUS UpdateMailboxHandler(EMRECORD FAR * pExRecord) {
VARARG_PTR ap = pExRecord->Ap;
DHANDLE hNote = VARARG_GET (ap, DHANDLE);
appendRichTextToBody(hNote, " Test Rich Text EM_NSFNOTEUPDATEMAILBOX BEFORE ");
return (EM_ERR_CONTINUE);
}
I don't see any appended text at recipient side.
But the mail in Sent folders has both appended texts.
I presume that this is a client-side EM. Most mail processing code that I'm familiar with operates on the server-side instead, by hooking EM_NSFNOTEUPDATE in the mail.box file on the server. The trick there, though, is that you must prevent the router from sending the message before you are finished processing it, so the very first thing you do is you change the status of the message to "On Hold" so the router won't even try to mail it. Then you can do your work on it, and finally release it. (Usually on the server side, this involves signalling a separate server task to do the work, as you don't want to keep a main server thread from returning from NSFNoteUpdate while the work is done.)
Even with EM_BEFORE processing, you may need to do something similar on the client side in order to accomplish this. You may at least want to add EM_BEFORE and EM_AFTER event handlers for EM_NSFNOTEUPDATE to monitor the mail.box file to determine whether those events are firing before or after the EM_MAILSENDNOTE event fires.
I know of Domino C API EMs that add signatures via server not client to outbound SMTP emails, so this must be possible.
EM_MAILSENDNOTE can only be used on a client.
BTW If this does not make sense here, its because of the restrictions on this site, I cannot add a comment with more detail, I cannot add a link, I cannot add code !!!