commit 21863d72416d3dca9d5ff1b4655c06c05850598b Author: Juhani Krekelä Date: Mon Apr 19 13:47:21 2021 +0300 Upstream tarball diff --git a/CHANGES b/CHANGES new file mode 100644 index 0000000..089fd95 --- /dev/null +++ b/CHANGES @@ -0,0 +1,965 @@ +2.0 +--- +alpha 19 - 19990530 +-------- +Cancelling a blocked source caused a NULL function to get called...should be + fixed. Changed chimera/source.c. +Portability changes for the NEC EWS4800/360PX. + Yozo Toda +Portability changes to make gcc quieter when compiling. Fixed function + type errors. Alexander Mai +NULL return from malloc() in common/mempool.c at least triggers an error + message now but doesn't handle it. +Fixed a problem which caused a cached entry to get bypassed. Changes made + to TryMemoryCache() in chimera/source.c. + +alpha 18 - 19990527 +------------------- +Fixed bug in proto/file.c that caused chimera to crash when large + directory listings were being processed. +Hostnames entered in the URL field or with the Open dialog are transformed + into http://hostname/. Change made to chimera/request.c. + +alpha 16/17 - 19990408 +---------------------- +Added to the beginning of the key bindings so that they didn't + conflict with regular keystrokes. + +alpha 15 - 19980517 +------------------- +Reload flag was ignored in SinkCreate() in chimera/source.c which caused + reloads to fail if the document was in core. +Started hacking on CSS parser thing. Created html/css.c and html/css.h + with some integration with existing html/ code. +Image load concurrance increased in html/inline.c. +Added patch to chimera/resource.c. Content could be accessed + uninitialized in TryMemoryCache(). + smarry@pantransit.smar.reptiles.org +Problem with uninitialized variable in AddObject() in htm/html.c. + smarry@pantransit.smar.reptiles.org +Localhost and hostname weren't allowed as proxy hosts. This caused + trouble with local proxies like wwwoffle. +wr->pup wasn't being passed to HTTPCreateInfo() from HTTPInit(). +The request sent to HTTP proxies was not complete. Fixed HTTPGetFilename(). +Keyboard accelerators added. Changes in chimera/WWW.c and chimera/head.c + Andrew M. Bishop + +alpha 14 - 19980103 +------------------- +Fixed the authorization handling code in proto/http.c that was + broken in the previous release. +Included string.h in common/mime.c + Alexander Mai +Modified chimera/Imakefile to work better with OS/2. + Alexander Mai +Worked on chimera/source.c to make handling of downloads less buggy. +Worked on chimera/view.c and chimera/stack.c to make handling of + external viewers less buggy. + +alpha 13 - 19971217 +------------------- +Changed common/mime.c to do a better job of parsing the headers that + appear in MIMEish streams. Required modifications to other + bits of code that called the functions in common/mime.c. +Added patch to fix portability problems in image/jpegp.h and image/jpeg.c. + Tom Lane +Removed buggy error checking abort() thing in html/html.c. + Tom Lane +Code in proto/http.c reworked a bit to account for changes in common/mime.c + and to simplify it a bit. + +alpha 12 - 19971211 +------------------- +Added mention that app-defaults files should be avoided for v2 and that + a wrapper should be created if v1 and v2 are being used on the + same system to point v2 away from the v1 app-defaults file. +Added patch to html/misc.c to fix problems with bold and strong text. + Russell King +Chimera now looks at the last argument on the command line as the URL. + Regis Rampnoux +Added patches for missing common.h in files in image/ + Alexander Mai +Fixed prototype in chimera/ChimeraSource.h + "Nelson H. F. Beebe" +Patch to frame code in html/frame.c. + Dave Davey + +alpha 11 +-------- +Fixed memory leak at chimera/stack.c:280. Frame-related memory leak still + exists. +Fixed access of NULL ptr caused by lack of rows/cols attribute in frameset + in html/frame.c. + +alpha 10 +-------- +Added patch for EMX/OS2. Alexander Mai +Added patch to allow filenames as the initial URL. +Added patch to fix relative URLs for redirects in proto/http.c. + Julian Coleman + +alpha 9 +------- +Fixed HTMLTitleEnd in html/head.c. +Patch for QNX in common/uproc.c and bug in proto/http.c. + "D. J. Hawkey Jr." +Fixed initialization of parent_url in chimera/request.c. + Dave Davey +Resource user.email added to use as value for the From: field in + proto/mailto.c. + +alpha 8 +------- +prototype for snprintf() added to port_after.h. trouble expected. +more prototypes added. +ChimeraRequest destroyed twice in chimera/source.c. + "Bradley G. Kaiser" +Added font fixup code to html/font.c + "Bradley G. Kaiser" +Tag IDs are now compared instead of tag structure addresses in html/html.c + which fixes environment disaster bug spotted by + john kraft +NULL filename part in URL caused URLs to be resolved incorrectly. + common/url.c:resolve_filename() changed. +Added mailto code from. Big win. + Dave Davey +Resource added to allow new head to get triggered on a type of URL. + Example: mailto.newhead: true +Resource added to allow the user to specify something besides sendmail + in proto/mailto.c. + +alpha 7 +------- +Added "find" button. +Added string find capability to plain text renderer. Bg/fg color broken. +Changed search and query callbacks so they aren't such a pain to deal with. +Cleaned up some compiler complaints about functions without prototypes. + +alpha 6 +------- +RequestCreate in chimera/request.c now properly calls the custom URL + parser callbacks and allows a URL to be handled completely + by a proxy. +Moved architecture specific debugging flag selections from the toplevel + Imakefile to Common.tmpl.dist. +Added patch to html/form.c to fix problem with call to RenderAction. + Erik Johannessen +Patch provided so that if the path is not defined by the resource + view.path then the PATH environment variable is used when + the path for external programs is specified. Also, added + '/' in mytmpnam in common/util.c. + Sander van Malssen +Added '()' to the command passed to system() in chimera/view.c. +Added popup to display the progress of downloads that will be viewed + with external viewers and to allow them to be cancelled. +Frame 'src' had to be an absolute URL. Now it can be a relative URL. +Made other random hacks in chimera/ and html/ to make the + target attribute work. Whoops, it doesn't work yet. +Form image submit works better. + +alpha 5 +------- +More work on HTML frames. +Added various patches by J.D.Coleman@newcastle.ac.uk +Fixed problems with using FILENAME_MAX. Reported by + roessner@rbg.informatik.th-darmstadt.de +External viewers now work. Bug reported by + Yozo Toda +Moving to name anchors should work now. +The previous position of a document should be remembered correctly now. +HTTP basic authentication should work. + +alpha 4 +------- +Moved files from www/ and main/ into chimera/. Reworked stuff and + gratituously changed names to satisfy my inner child. +Added HTML frames. + +alpha 3 +------- +Added '#include ' to common/mime.c. +Removed (XPointer *) from mxw/TextField.c. Older versions of X don't + define XPointer. + +alpha 2 +------- +Added patch to www/io.c for HPUX. + Tom Lane +Added new jpeg.c and jpegp.h. + Tom Lane +Added linebreak after
+Fixed problem in html/flow.c:WidthFlow(). Width was not calculated + correctly after linebreak. +Added code in html/flow.c:LayoutFlow() to make
work. +Fixed a few type mismatches lurking in html/*.c. +Added modifier for library names in Common.tmpl.dist in case system + library names clash. +Stuck 'x' on the front of chimera library names to prevent clashes. + +alpha 1 +------- +Added patch to html/font.c to make font weight selection better. + Jean-Marc Lasgouttes +Added patch to HTMLTDInsert() to deal with missing correctly. +Added another tag handling kludge function to HTMLTag. This one is used + to figure out how to clamp down end tags for things like
    + and . + +cfh-2.0 +------- +alpha 171-190 +------------- +Entities are case sensitive. Changed strncasecmp to strncmp. + Erik +Fixed code in common/mime.c to find the HTTP status. +Fixed code for setting path in main/view.c. +Various fixes from "D. J. Hawkey Jr." for + PRE tags, URL handling, and QNX. +Created a bunch of resource access functions to make it easier to access + resource values. Made cache resource dealings more reasonable + as suggested by Yozo Toda +Added chimera.urlLogFile resource. If set to a valid writable file will + append a log of URLs that were loaded at the toplevel. +Discovered reentrancy problem in main/download.c. + Same problem in main/view.c. (should be fixed now) + Smarasderagd +Removed JCONFIG_INCLUDED from image/jpeg.c because it caused definitions + to get messed up in jpeg.h. Tom Lane +Resources should now be correctly free'd when a head is destroyed in + main/widget.c. +Added support for 'Host' field in proto/http.c. +Fixed a bunch of memory leaks (but not even close to all of them). + Added tracking code to common/list.c and common/mempool.c. +Added a 'reusable' flag to the Scheme structure in www/data.c to indicate + cached documents can be reused without setting the cache + flag which would cause them to be recached. +Added argument to WWWCancelData() to allow a distinction between cancelling + callbacks and actually cancelling the data. Also hacked around + to make sure the code kept track of downloads more correctly. +Added code to html/inline.c to make sure that when the reload flag is set + that an inline is only reloaded once. +Added code to www/context.c to look for the www.maxDownloads resource to + set the maximum number of concurrent downloads. +Added/fixed the code that actually limits the number of concurrent downloads. + Whoops. +FTP code communicates failures back up the handler chain. Probably needs + more work but should work better. +Code in common/ml.c took a dump when untagged text showed up. + Should be fixed now. +The accept list is now taken in account when searching to see if a current + download can be used instead of starting a new download. See + TryMemoryCache in data.c for details. +Added code to look at www.printLoadMessages resource. +Markup language parser code in common/ml.c revised to work a bit better. + Mostly changed to make comment handling work better. +Patch to add keyboard editting commands to the text widget. Joy! + "J.D.Coleman" +Added code to highlight tags when selected. +If the first document is not HTML then no HTML fonts are created but + the HTML class code still tried to free them. Should be fixed now. +Added config for AIX 3.2.5. + mark olesen +Lots of reworked code in html/. + +alpha 169-170 +------------- +-lsocket -lnsl added to OS_LIBS in Common.tmpl.dist for Solaris. + Yozo Toda +Modified Common.tmpl.dist install directories as suggested by + Jean-Marc.Lasgouttes@inria.fr +Made config changes for QNX. "D. J. Hawkey Jr." +Switched URLParse back to putting NULL in filename if it is zero-length. + Doesn't change it if it is NULL. +Changed the file: code to change a NULL filename to "/". +Added endianism for 68xxx. David Wolfskill +Changed column scaling in html/table.c:TablePosition1. +Scaled box width didn't include the width of all of the columns when + colspan > 1. Changed html/table.c:TablePosition1. +Fixed another problem in common/mempool.c and rearranged things a bit. +Worked on main/download.c to make downloads behave a little better. +Added patch for 1x1 interlaced GIF problem. + Smarasderagd +Changed the MIME code to try to make it a bit faster. Changes had to + be made all over the place. + +alpha 166-168 +------------- +Added file.autoLoad resource. If defined then the file protocol checks + to see if the URL specifies a directory and, if it does, checks to + see if the file specified in file.autoLoad exists in that + directory and loads it instead of a directory listing. +Added patch to fix alignment problem in mempool.c. + "J.D.Coleman" +Code in proto/http.c no longer cares about the useRedirect resource. +Let HTTP error messages display instead of trying to trap the messages + in proto/http.c. +Fixed cache flag bug in proto/http.c. +Removed -Wmissing-* because it caused old versions of gcc to become upset. +Made sure inputs for forms are removed from any lists they might be + in when they're destroyed because the two-pass thing that + happens for tables causes a whole heap of trouble. +If a cache directory can't be found then a message is printed on stderr. +The environment variable WWW_HOME can be used to specify the first document. +URLParse always returns "/" instead of a NULL or zero-length filename. +Listened to Jonny Quest theme by Reverend Horton Heat. Liked it. + +alpha 165 +--------- +Added __convex__ to image/image_endian.h. Added ConvexArchitecture to + Common.tmpl.dist. David DeSimone +Hopefully Common.tmpl.dist will deal with Solaris more reasonably. + Help from lots of people from bug-chimera. +Added HPArchitecture based on tip from + Otmar Stahl +Cut buffer cleared if there is a valid URL inside that is used to + start a new shell. +Fixed problem with horizontal scrollbar introduced when the + trouble-causing arguments were removed from www/WWW.c +Replaced 'clock' with 'time' in common/util.c +Added DIV tag to html/misc.c. +Finally found a really annoying bug in the table code. Rowspan > 1 + in the middle of a row caused trouble. +Dialogs now have one label area instead of two. + +alpha 164 +--------- +Switched over to Alignment struct/union thing in common/mempool.c. + Markku Savela . +Got rid of trouble-causing arguments in www/WWW.c. + +alpha 163 +--------- +Commented out table debugging code. +Re-enabled comment parsing in common/ml.c. +common/mempool.c uses MEMPOOL_ALIGNSIZE to determine the alignment size. + The default is sizeof(size_t). +Boxes accessing related data after the data was destroyed causing + seg violation in html/module.c:HTMLDestroy. Do box destruction + first. +Messages for downloading and other activities should appear in the + shell(s) that apply. Added extra field to the file dialog so that + file operation messages will appear in the right spot. +Patch for URL escaping in common/url.c and html/form.c + Jake Kesinger +Patch for broken font handling in plain/plain.c + "T. Alexander Popiel" +Fakes before looks bad but shouldn't cause a core file + to appear. +Missing 'l' in 'lenv' in HTMLOListEnd in html/list.c. + Added NetBSDArchitecture to Common.tmpl.dist + Added missing definitions to common/common.h for functions in + common/list.c "J.D.Coleman" +Fixed other random table weirdness (my powers of description are stretched + to the max with some of this sludge I'm writing). + +alpha 112-161 +------------- +Lots of code reworked, moved around, and other fun stuff. +Added GPL. +Added Rob McMullen's TextField widget. +Incorporated patch for numbering ordered lists. + Jake Kesinger ? +Added patch for dealing with malloc failures to the image/ code. + Smarasderagd +Frames disappeared + +alpha 111 +--------- +Made changes with the way frame position is set. The position of + a document is passed to the renderer for it to deal with. +Fixed anchor handling in the HTML code. +Added for check "id" attribute. Erik Corry +The HTML code now has a timeout that will destroy an inline if it + takes too long to find its size. +Moved the cache code into www/. + +alpha 110 +--------- +Frames are refreshed when they are resized. +Made changes to html/* to improve HTML handling. +Comments are handled slightly differently in common/ml.c. +FreeBSD added to Common.tmpl.dist. +Added support for the dreaded
    tag. + +alpha 109 +--------- +Fixed MEMPOOL_DEBUG usage in the defintion of pool. common/mempool.c. +NULL pointer accessed in www/frame.c:FrameQuery() sometimes when + source requested. Ben Taylor +Added http.useRedirect resource which makes HTTP 302s act like 301s. +Fixed SELECT form problems and cleaned up the code. Did the same for + the textarea code. +Fixed MxwGetFont()...was always returning NULL. +Reworked CallTagHandler() a bit. +XMP added...acts like PRE. +Clone will really do a clone now...the URL is taken from the cloned + window. The resource chimera.cloneHome can be set to force the + clone to use the home page. +Listing and XMP should work correctly now. +HTML code reworked a bit. +Line 235 in www/frame.c used frame->fcontext instead of value like it + was supposed to. +Fixed a problem with bookmarks...the element handler wasn't handling + elements correctly. + +alpha 108 +--------- +Fixed problems in html/form.c. Reworked things a bit. Still in bad + shape. + +alpha 107 +--------- +Changed proportional font default pattern in html/font.c. Big win. + Rob McMullen +Added patch to fix 'ENDIAN' preprocessor goodies in image/ and + fix homeless startup crash. Erik Corry +Cleaned up frame and proto handling code in www/proto.c and www/frame.c +Made the connection count code in www/proto.c work. +Pressing "source" won't cause a reload. Created FrameRedisplay(). +Doing a reload caused all subsequent loads to inherit the reload flag. +Put a line in Common.tmpl to show where you can force a special compiler. +Added margins to the HTML renderer. Defaults to 20. +Tags are handled the old way...if tags are overlapped then end tags + are simulated to eliminate overlap. +Changed font code so that the fixed fonts work the same way as the + proportional fonts. +Fixed bookmark create problems. Strings were not being allocated + correctly...they were auto vars that got wiped out. +Worked on the markup parsing. Seems to handle comments and stuff in + general more correctly. Simplified Element handling and made all of + the 'common/ml' types opaque. More function calls! Less mucking + with internals! +Worked on HTML list handling...didn't handle bad HTML as well as it + could. +Added better isspace() called isspace8(). + Erik Corry + +alpha 106 +--------- +Parentless frames needed a fake parent ID to prevent collions with + real parent IDs. Solves all sorts of terrible problems. +Added external viewer code. Had to rearrange www/proto.c, www/frame.c, and + create src/view.c. Should be faster. +Made a whole bunch of changes to the HTML renderer to better deal with + errors and to a problem that caused paragraphs (and other tags) to wipe + out previous tag environments. +Redirect URLs are correctly passed back from protocol handlers to the URL + stack. HTTP passes it back for status "Moved permanently" only. +Forms fixed. Multi-selects work internally (how useful!). Form image + works. +Won't crash if no bookmarks are available. +Cache now uses an integer ID for the filename that is kept in the + cache index. Unfortunately, you can only have 2^31 cache entries + on most machines. +HTTP code was looking for the location: field and ignoring the status. + Nexus server returns location field even when status was not 300-399. + This caused a nifty loop. +GET /something?mumble queries failed because it was assumed to be NULL + terminated but wasn't necessarily NULL terminated. +FrameDestroy() was not free'ing everything. +Bookmark now works when there is no bookmark file. + +alpha 104-105 +------------- +InitModule_Message() was not initializing ProtoHooks correctly. + Smarasderagd +Source button works now. +Simplified the code in www/io.c...it was getting too hairy without + providing any benefits. +Renamed functions in www/ and other places to something more reasonable. +Changed the behavior of MLMultiGetText() so that it is more reasonable + to use. +Reworked the forms code a bit: textarea and option text collection + is simpler. +I note the following typo: + cfh102-2.0/www/mime.c: { image/x-xpimap, xpm }, + Michael Kellen +Moved the stuff in misc/ to proto/. +Updated README.hints. +Changed the download code so that a save file requester appears at the + end of the transfer instead of an entire screen in the main display. +
    causes a line break. +HTMLQuery() in module/html.c could not deal with a NULL title. +Rearranged and messed around with code to try to get the memory + usage down a bit. +Bookmark code now works pretty much the way it did in 1.65. Bookmark file + is an HTML-like file. changes in src/ +Added resource to set the maximum length of a cache filename length. + See lib/resources for details. I can't remember who asked me for this. +Messed around with Common.tmpl and the Imakefiles to make it easier for + me. +Fixed line layout code to take baselines into account to make + the "align" attribute work. Changed a bunch of stuff in html/ +*/* is added to the end of the accept string. Changed proto/http.c. +URL resolution fixed and simplified slightly. '..' caused problems + and things like '//' were not removed which messed up the cache. + Added common/dir.c and modified www/url.c. +Made lots of worthless cosmetic changes to the functions in html/. +Added check for _SSIZE_T and _SIZE_T to common/common.h +Added image input field to forms support. +Fixed FTP. Broke in 103. +InitModule_File wasn't initializing structure properly. proto/file.c changed. +Forms code memory overrun. html/form.c changed. + +alpha 103 +--------- +Readded code to use the base filename of a URL as the default name + for saving stuff. +aXe scrolling text widget removed. +The title was not being terminated at the right time in AddBookmark() in + src/bookmark.c. +Support for tag added. + Jake Kesinger + +alpha 102 +--------- +A colon delimited list of filenames can be specified for the dbFiles + resource. +URL failure was not being reported back to the parent frame. This caused + HTML rendering to stop forever waiting for the image. Partially fixed. + Reported by Karl Eichwalder . +Added support for the size attribute for form text input. + Larry Doolittle +Paragraph align attribute was being carried over into the following + paragraphs if the aligned paragraph didn't have an end tag. Fixed. + Reported by Karl Eichwalder +Feeling silly...added some support for frames. html/frame.c. +Cleaned up the URL load code in HTML and shoved it in html/load.c. +HandleSelect() was causing trouble if called for but there + was never a
    or more reasonably. +Fixed bad declaration for HTMLAttributeToID in html/html.h. + Steven E Lumos +Made Common.tmpl.dist style a bit more consistent. +Added patch submitted in 1994(!) to map URLs to other URLs using + regular expressions. Theodore Ts'o + This has been disabled for now for performance reasons. +When a new head/shell is started the cut buffer is examined for a URL. + If there is a valid URL at the beginning then it will be loaded. + Changed main/main.c. +In url.c:URLParse look for other separators not just '/' before ':'. +Revampled www/request.c:WWWBuildRequest to make it more efficient hopefully. +Baseline should be fixed for forms widgets. +Switched over to using snprintf and fixed size buffers in a few + places. Mostly in message handling. Included code for snprintf + from LPRng-2.4.2. +www/mime.c:MIMEFindData searched one byte too far which caused really + all sorts of irritating trouble. + +alpha 162 +--------- +Added LinuxArchitecture to Common.tmpl.dist +Proportional scaling of table columns added to html/table.c. +Added BSD386Architecture to Common.tmpl.dist +Baseline code for inline images fixed. +Referenced NULL pointer in SubmitCallback fixed in html/form.c. +Table width was ending up as 0 (probably a bug) which was later used + in a division. Fixed to not cause SIGFPE. +All traces of the app-defaults file removed (hopefully). +Right floating objects should now float to the right hand side even if there + aren't enough objects to push it to the edge. + or before a
    tag is encountered + */ +void +HTMLTDBegin(li, env, p) +HTMLInfo li; +HTMLEnv env; +MLElement p; +{ + HTable *ts; + HRow *rs; + HData *ds; + + ts = (HTable *)env->penv->closure; + env->closure = ts; + + if (ts->pass == 0) + { + rs = (HRow *)GListGetTail(ts->grid); + myassert(rs != NULL, "Could not find row data (1)."); + + ds = (HData *)MPCGet(ts->mp, sizeof(HData)); + if (p == NULL) + { + ds->colspan = 1; + ds->rowspan = 1; + } + else + { + if ((ds->colspan = MLAttributeToInt(p, "colspan")) < 1) ds->colspan = 1; + if ((ds->rowspan = MLAttributeToInt(p, "rowspan")) < 1) ds->rowspan = 1; + } + + GListAddTail(rs->datas, ds); + + rs->colcount += ds->colspan; + if (rs->colcount > ts->colcount) ts->colcount = rs->colcount; + /* this is more than the real count but that's ok. */ + ts->rowcount += ds->rowspan; + + ts->dbox = ds->box = HTMLCreateFlowBox(li, env, li->tableCellInfinity); + } + else + { + rs = (HRow *)GListGetCurrent(ts->grid); + myassert(rs != NULL, "Could not find row data (2)."); + + ds = (HData *)GListGetCurrent(rs->datas); + myassert(ds != NULL, "Could not find cell data (2)."); + + ts->dbox = ds->box = HTMLCreateFlowBox(li, env, ds->width); + } + + GListAddHead(ts->klist, ds->box); + + return; +} + +/* + * HTMLTDEnd + * + * Called when a tag is encountered + */ +void +HTMLTDEnd(li, env, p) +HTMLInfo li; +HTMLEnv env; +MLElement p; +{ + HTable *ts = (HTable *)env->closure; + HRow *rs; + + HTMLFinishFlowBox(li, ts->dbox); + if (ts->pass > 0) + { + rs = (HRow *)GListGetCurrent(ts->grid); + GListGetNext(rs->datas); + } + return; +} + +/* + * HTMLTRBegin + */ +void +HTMLTRBegin(li, env, p) +HTMLInfo li; +HTMLEnv env; +MLElement p; +{ + HTable *ts; + HRow *rs; + + ts = (HTable *)env->penv->closure; + env->closure = ts; + + if (ts->pass == 0) + { + rs = (HRow *)MPCGet(ts->mp, sizeof(HRow)); + GListAddTail(ts->grid, rs); + rs->datas = GListCreateX(ts->mp); + } + else + { + rs = (HRow *)GListGetCurrent(ts->grid); + GListGetHead(rs->datas); + } + + return; +} + +/* + * HTMLTREnd + */ +void +HTMLTREnd(li, env, p) +HTMLInfo li; +HTMLEnv env; +MLElement p; +{ + HTable *ts; + + ts = (HTable *)env->penv->closure; + env->closure = ts; + if (ts->pass > 0) GListGetNext(ts->grid); + + return; +} + +/* + * HTMLTableBegin + * + * Called when is encountered. Has to deal with two passes. + */ +void +HTMLTableBegin(li, env, p) +HTMLInfo li; +HTMLEnv env; +MLElement p; +{ + HTMLBox box; + HTable *ts; + char *value, *cp; + int x; + int border; + unsigned int width, maxwidth; + + maxwidth = width = HTMLGetMaxWidth(li, env->penv); + + env->ff = FLOW_LEFT_JUSTIFY; + env->anchor = NULL; + + if (env->closure == NULL) + { + ts = (HTable *)MPCGet(li->mp, sizeof(HTable)); + ts->mp = li->mp; + ts->p = p; + env->closure = ts; + + if ((border = MLAttributeToInt(p, "border")) < 0) border = 0; + ts->border = border; + ts->grid = GListCreateX(ts->mp); + ts->klist = GListCreateX(li->mp); + + if ((value = MLFindAttribute(p, "width")) != NULL) + { + if (isdigit(value[0])) + { + x = atoi(value); + for (cp = value; ; cp++) + { + if (!isdigit(*cp)) break; + } + if (*cp != '\0') + { + if (*cp == '%') + { + if (x >= 10) width = width * x / 100; + } + else if (x >= 100) width = x; + } + else if (x >= 100) width = x; + } + } + if (width > 0) ts->max_width = width; + else ts->max_width = 0; + } + else + { + ts = (HTable *)env->closure; + GListGetHead(ts->grid); + GListClear(ts->klist); + } + ts->pass = env->pass; + + /* + * Create the table box + */ + ts->box = box = HTMLCreateBox(li, env); + box->setup = SetupTable; + box->render = RenderTable; + box->destroy = DestroyTable; + box->closure = ts; + + return; +} + +/* + * HTMLTableEnd + * + * Called when
    is encountered. Has to deal with two passes. + */ +void +HTMLTableEnd(li, env, p) +HTMLInfo li; +HTMLEnv env; +MLElement p; +{ + HTable *ts = (HTable *)env->closure; + + if (ts->colcount == 0) return; + + if (ts->pass == 0) + { + /* + * Do first pass size calculations. + */ + FillSpans(ts); + TablePosition1(ts); + + /* + * Now destroy the table box and its children since we need to rebuild + * from scratch. + */ + HTMLDestroyBox(li, ts->box); + ts->box = NULL; + } + else + { + TablePosition2(ts); + + HTMLEnvAddBox(li, env->penv, ts->box); + } + + return; +} + +/* + * FillSpans + */ +static void +FillSpans(ts) +HTable *ts; +{ + HRow *rs; + HData *ds; + int i, j, k; + HCell *pca; + + ts->colcount *= 2; + + /* + * Allocate an array for each row to keep track of the data grid. + */ + for (k = 0, rs = (HRow *)GListGetHead(ts->grid); rs != NULL; + rs = (HRow *)GListGetNext(ts->grid), k++) + { + rs->ca = (HCell *)MPCGet(ts->mp, sizeof(HCell) * ts->colcount); + } + /* + * Its possible rowspan has pushed things down a bit so add rows as + * necessary. Overkill is OK. + */ + for (; k < ts->rowcount; k++) + { + rs = (HRow *)MPCGet(ts->mp, sizeof(HRow)); + GListAddTail(ts->grid, rs); + rs->datas = GListCreateX(ts->mp); + rs->ca = (HCell *)MPCGet(ts->mp, sizeof(HCell) * ts->colcount); + } + + /* + * Put the data into the grids according to rowspan and colspan. + */ + pca = NULL; + for (rs = (HRow *)GListGetHead(ts->grid); rs != NULL; + rs = (HRow *)GListGetNext(ts->grid)) + { + /* + * If there is a previous row then look at the HCell's above and + * see if they need to be extended. We know the HCell above needs to + * be extended down if rowspan > 1. Move rowspan - 1 down to the + * current row. Also, move the colspan + * value down (its not used, though) and the HData pointer. + */ + if (pca != NULL) + { + for (i = 0; i < ts->colcount; i++) + { + if (pca[i].ds != NULL && pca[i].rowspan > 1) + { + myassert(rs->ca[i].ds == NULL, "Cell visitation unexpected!"); + rs->ca[i].rowspan = pca[i].rowspan - 1; + rs->ca[i].colspan = pca[i].colspan; + rs->ca[i].ds = pca[i].ds; + } + if (pca[i].ds2 != NULL && pca[i].rowspan2 > 1) + { + myassert(rs->ca[i].ds == NULL, "Cell visitation unexpected! (2)"); + rs->ca[i].rowspan = pca[i].rowspan2 - 1; + rs->ca[i].colspan = pca[i].colspan2; + rs->ca[i].ds = pca[i].ds2; + } + } + } + + /* + * Now extend HData to the right depending on the colspan. + */ + for (ds = (HData *)GListGetHead(rs->datas); ds != NULL; + ds = (HData *)GListGetNext(rs->datas)) + { + /* + * Scan to the right and look for the first empty HCell. This needs + * to be done because an HData above could have spanned down to fill + * in the beginning of the row. + * + * This has to be done for each data section because a row that + * was extended down may have used up space in the middle of the + * row. + */ + for (i = 0; i < ts->colcount; i++) + { + if (rs->ca[i].ds == NULL) break; + } + myassert(ts->colcount != i, "Table column count confused."); + + rs->ca[i].original = true; + for (j = 0; j < ds->colspan; j++, i++) + { + if (rs->ca[i].ds == NULL) + { + rs->ca[i].ds = ds; + rs->ca[i].colspan = ds->colspan - j; + rs->ca[i].rowspan = ds->rowspan; + } + else + { + rs->ca[i].ds2 = ds; + rs->ca[i].colspan2 = ds->colspan - j; + rs->ca[i].rowspan2 = ds->rowspan; + } + } + } + + pca = rs->ca; + } + +/* + * Test code. Print out patterns to see if the internal representation + * looks reasonable. + */ +/* + for (rs = (HRow *)GListGetHead(ts->grid); rs != NULL; + rs = (HRow *)GListGetNext(ts->grid)) + { + for (i = 0; i < ts->colcount; i++) + { + if (rs->ca[i].ds2 != NULL) printf ("o"); + else if (rs->ca[i].ds != NULL && rs->ca[i].original) + { + printf ("%c", (char )((int)('A') + i)); + } + else if (rs->ca[i].ds != NULL) printf ("%c", (char )((int)('a') + i)); + else printf ("x"); + } + printf("\n"); + } +*/ + + return; +} + +/* + * TablePosition1 + */ +static void +TablePosition1(ts) +HTable *ts; +{ + int i, j; + HRow *rs; + HData *ds; + unsigned int twidth, width; + + /* + * Allocate arrays to hold the column widths and row heights. + * + * cwidth = column width + * swidth = scaled column width + * rheight = row height + */ + ts->cwidth = (unsigned int *)MPCGet(ts->mp, sizeof(unsigned int) * + ts->colcount); + ts->swidth = (unsigned int *)MPCGet(ts->mp, sizeof(unsigned int) * + ts->colcount); + ts->rheight = (unsigned int *)MPCGet(ts->mp, sizeof(unsigned int) * + ts->rowcount); + + /* + * Figure out the unrestrained widths of the columns. Needed a little + * later to determine the width of the table. + */ + twidth = 0; + for (rs = (HRow *)GListGetHead(ts->grid); rs != NULL; + rs = (HRow *)GListGetNext(ts->grid)) + { + for (i = 0; i < ts->colcount; i++) + { + if (rs->ca[i].ds != NULL) + { + ds = rs->ca[i].ds; + width = ds->box->width / ds->colspan; + if (width > ts->cwidth[i]) ts->cwidth[i] = width; + } + if (rs->ca[i].ds2 != NULL) + { + ds = rs->ca[i].ds2; + width = ds->box->width / ds->colspan; + if (width > ts->cwidth[i]) ts->cwidth[i] = width; + } + } + } + + /* + * Determine the width of the unrestrained table. + */ + twidth = 0; + for (i = 0; i < ts->colcount; i++) + { + twidth += ts->cwidth[i]; + } + + if (twidth > 0 && twidth > ts->max_width) + { + /* + * Now scale the columns to fit in the space available attempting to keep + * things in proportion. + */ + for (i = 0; i < ts->colcount; i++) + { + ts->swidth[i] = ts->max_width * ts->cwidth[i] / twidth; + } + } + else + { + /* + * No size problems so just copy the widths as-is. + */ + for (i = 0; i < ts->colcount; i++) + { + ts->swidth[i] = ts->cwidth[i]; + } + } + + /* + * Finally, figure out the widths of individual cells. + */ + for (rs = (HRow *)GListGetHead(ts->grid); rs != NULL; + rs = (HRow *)GListGetNext(ts->grid)) + { + for (i = 0; i < ts->colcount; i++) + { + if (rs->ca[i].ds != NULL && rs->ca[i].original) + { + /* + * Add in the widths of all the columns for a column spanning + * cell. + */ + ds = rs->ca[i].ds; + for (j = 0; j + i < ts->colcount && j < ds->colspan; j++) + { + ds->width += ts->swidth[i + j]; + } + } + } + } + + return; +} + +/* + * TablePosition2 + */ +static void +TablePosition2(ts) +HTable *ts; +{ + int i, j; + unsigned int width, height; + HRow *rs; + HData *ds; + + memset(ts->cwidth, 0, ts->colcount * sizeof(unsigned int)); + memset(ts->rheight, 0, ts->rowcount * sizeof(unsigned int)); + + /* + * Get the columns widths and row heights for the restrained table + * (second pass). + */ + for (j = 0, rs = (HRow *)GListGetHead(ts->grid); rs != NULL; + rs = (HRow *)GListGetNext(ts->grid), j++) + { + for (i = 0; i < ts->colcount; i++) + { + if (rs->ca[i].ds != NULL) + { + ds = rs->ca[i].ds; + width = ds->box->width / ds->colspan; + if (width > ts->cwidth[i]) ts->cwidth[i] = width; + + height = ds->box->height / ds->rowspan; + if (height > ts->rheight[j]) ts->rheight[j] = height; + } + + if (rs->ca[i].ds2 != NULL) + { + ds = rs->ca[i].ds2; + width = ds->box->width / ds->colspan; + if (width > ts->cwidth[i]) ts->cwidth[i] = width; + + height = ds->box->height / ds->rowspan; + if (height > ts->rheight[j]) ts->rheight[j] = height; + } + } + } + + /* + * Find the final width and height of the table. + */ + width = 0; + height = 0; + for (i = 0; i < ts->colcount; i++) + { + width += ts->cwidth[i]; + } + for (i = 0; i < ts->rowcount; i++) + { + height += ts->rheight[i]; + } + + ts->box->width = width; + ts->box->height = height; + + return; +} + +/* + * HTMLTableAccept + */ +bool +HTMLTableAccept(li, obj) +HTMLInfo li; +HTMLObject obj; +{ + if (obj->type != HTML_ENV) return(false); + if (HTMLTagToID(obj->o.env->tag) != TAG_TR) return(false); + return(true); +} + +/* + * HTMLTRAccept + */ +bool +HTMLTRAccept(li, obj) +HTMLInfo li; +HTMLObject obj; +{ + HTMLTagID tagid; + + if (obj->type != HTML_ENV) return(false); + tagid = HTMLTagToID(obj->o.env->tag); + if (tagid != TAG_TD && tagid != TAG_TH) return(false); + + return(true); +} + +/* + * HTMLTDInsert + */ +HTMLInsertStatus +HTMLTDInsert(li, env, p) +HTMLInfo li; +HTMLEnv env; +MLElement p; +{ + HTMLEnv c; + HTMLTagID tid; + + for (c = (HTMLEnv)GListGetHead(li->envstack); c != NULL; + c = (HTMLEnv)GListGetNext(li->envstack)) + { + tid = HTMLTagToID(c->tag); + if (tid == TAG_TABLE || tid == TAG_TR) break; + } + + if (c == NULL) return(HTMLInsertReject); + else if (tid == TAG_TABLE) HTMLStartEnv(li, TAG_TR, NULL); + else HTMLPopEnv(li, TAG_TR); + + return(HTMLInsertOK); +} + +/* + * HTMLTDClamp + */ +bool +HTMLTDClamp(li, env) +HTMLInfo li; +HTMLEnv env; +{ + HTMLEnv c; + HTMLTagID tid; + + for (c = (HTMLEnv)GListGetHead(li->envstack); c != NULL; + c = (HTMLEnv)GListGetNext(li->envstack)) + { + tid = HTMLTagToID(c->tag); + if (tid == TAG_TABLE || tid == TAG_TD) break; + } + if (c == NULL) return(false); + else if (tid == TAG_TABLE) return(false); + + return(true); +} + +/* + * HTMLTRInsert + */ +HTMLInsertStatus +HTMLTRInsert(li, env, p) +HTMLInfo li; +HTMLEnv env; +MLElement p; +{ + HTMLEnv tenv; + + if ((tenv = HTMLFindEnv(li, TAG_TABLE)) == NULL) return(HTMLInsertReject); + + HTMLPopEnv(li, TAG_TABLE); + + return(HTMLInsertOK); +} + +/* + * HTMLTRClamp + */ +bool +HTMLTRClamp(li, env) +HTMLInfo li; +HTMLEnv env; +{ + HTMLEnv c; + HTMLTagID tid; + + for (c = (HTMLEnv)GListGetHead(li->envstack); c != NULL; + c = (HTMLEnv)GListGetNext(li->envstack)) + { + tid = HTMLTagToID(c->tag); + if (tid == TAG_TABLE || tid == TAG_TR) break; + } + if (c == NULL) return(false); + else if (tid == TAG_TABLE) return(false); + + return(true); +} + +/* + * HTMLTDWidth + */ +unsigned int +HTMLTDWidth(li, env) +HTMLInfo li; +HTMLEnv env; +{ + HTable *ts = (HTable *)env->closure; + return(HTMLGetBoxWidth(li, ts->dbox)); +} + diff --git a/html/text.c b/html/text.c new file mode 100644 index 0000000..b376e00 --- /dev/null +++ b/html/text.c @@ -0,0 +1,489 @@ +/* + * text.c + * + * libhtml - HTML->X renderer + * + * Copyright (c) 1995-1997, John Kilburg + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +#include "port_before.h" + +#include +#include + +#ifdef HAVE_STDLIB_H +#include +#endif + +#include "port_after.h" + +#include "html.h" + +#define TAB_EXPANSION 4 + +typedef struct TextStateP +{ + char *s; + size_t len; +} TextState; + +/* + * + * Private functions + * + */ +static void RenderText _ArgProto((HTMLInfo, HTMLBox, Region)); +static char *ExpandTabs _ArgProto((HTMLInfo, char *, size_t, size_t)); +static void LineMode _ArgProto((HTMLInfo, HTMLEnv, MLElement)); +static void HTMLPreformatted _ArgProto((HTMLInfo, HTMLEnv, char *, size_t)); + +/* + * RenderText + */ +static void +RenderText(li, box, r) +HTMLInfo li; +HTMLBox box; +Region r; +{ + TextState *ts = (TextState *)box->closure; + int x, y; + XFontStruct *font = HTMLGetFont(li, box->env); + + XSetFont(li->dpy, li->gc, font->fid); + + /* y position + ascent to line up correctly + descent for line spacing */ + y = box->y + font->ascent; + x = box->x; + + if (box->env->anchor != NULL) + { + if (HTMLTestB(box, BOX_SELECT)) + { + XSetForeground(li->dpy, li->gc, li->anchor_select_color); + } + else XSetForeground(li->dpy, li->gc, li->anchor_color); + + XDrawString(li->dpy, li->win, li->gc, x, y, ts->s, (int)ts->len); + XDrawLine(li->dpy, li->win, li->gc, x, y + font->descent - 1, + x + box->width, y + font->descent - 1); + + XSetForeground(li->dpy, li->gc, li->fg); + } + else + { + XDrawString(li->dpy, li->win, li->gc, x, y, ts->s, (int)ts->len); + } + + return; +} + +/* + * HTMLCreateTextBox + */ +HTMLBox +HTMLCreateTextBox(li, env, s, slen) +HTMLInfo li; +HTMLEnv env; +char *s; +size_t slen; +{ + HTMLBox box; + TextState *ts; + HTMLBoxFlags bflags; + XFontStruct *font = HTMLGetFont(li, env); + + if (s == NULL) + { + s = " "; + slen = 1; + bflags = BOX_SPACE; + } + else + { + bflags = BOX_NONE; + } + + ts = (TextState *)MPCGet(li->mp, sizeof(TextState)); + ts->s = s; + ts->len = slen; + + box = HTMLCreateBox(li, env); + HTMLSetB(box, bflags); + box->closure = ts; + box->render = RenderText; + box->width = XTextWidth(font, s, slen); + box->height = font->ascent + font->descent + li->textLineSpace; + box->baseline = font->ascent + li->textLineSpace; + + return(box); +} + +/* + * HTMLPreformatted - basically, the guts of LineMode() + */ +static void +HTMLPreformatted(li, env, text, len) +HTMLInfo li; +HTMLEnv env; +char *text; +size_t len; +{ + char *cp, *lastcp, *start; + size_t ptlen; + char *s; + bool tabbed; + + lastcp = text + len; + tabbed = false; + for (ptlen = 0, start = cp = text; cp < lastcp; cp++) + { + if (*cp == '\r') continue; + else if (*cp == '\n') + { + if (tabbed) + { + s = ExpandTabs(li, start, cp - start, ptlen); + HTMLEnvAddBox(li, env, HTMLCreateTextBox(li, env, s, ptlen)); + } + else + { + HTMLEnvAddBox(li, env, HTMLCreateTextBox(li, env, start, ptlen)); + } + + HTMLAddLineBreak(li, env); + + ptlen = 0; + start = cp + 1; + tabbed = false; + } + else if (*cp == '\t') + { + ptlen += TAB_EXPANSION; + tabbed = true; + } + else ptlen++; + } + + if (tabbed) + { + s = ExpandTabs(li, start, cp - start, ptlen); + HTMLEnvAddBox(li, env, HTMLCreateTextBox(li, env, s, ptlen)); + } + else + { + HTMLEnvAddBox(li, env, HTMLCreateTextBox(li, env, start, ptlen)); + } + + return; +} + +/* + * + * Public functions + * + */ + +/* + * ExpandTabs + */ +static char * +ExpandTabs(li, t, len, tlen) +HTMLInfo li; +char *t; +size_t len; +size_t tlen; +{ + char *s, *cp, *lastcp, *xs; + + lastcp = t + len; + s = (char *)MPGet(li->mp, tlen); + for (cp = t, xs = s; cp < lastcp; cp++) + { + if (*cp == '\t') + { + memset(xs, ' ', TAB_EXPANSION); + xs += TAB_EXPANSION; + } + else if (*cp != '\r') + { + *xs = *cp; + xs++; + } + } + + return(s); +} + +/* + * LineMode + */ +static void +LineMode(li, env, p) +HTMLInfo li; +HTMLEnv env; +MLElement p; +{ + char *text; + size_t len; + + MLGetText(p, &text, &len); + if (text == NULL) return; + + HTMLPreformatted(li, env, text, len); + + return; +} + +/* + * HTMLPlainData + */ +void +HTMLPlainData(li, env, p) +HTMLInfo li; +HTMLEnv env; +MLElement p; +{ + LineMode(li, env, p); + return; +} + +/* + * HTMLPreData + */ +void +HTMLPreData(li, env, p) +HTMLInfo li; +HTMLEnv env; +MLElement p; +{ + LineMode(li, env, p); + return; +} + +/* + * HTMLFillData + */ +void +HTMLFillData(li, env, p) +HTMLInfo li; +HTMLEnv env; +MLElement p; +{ + char *s, *cp, *lastcp; + bool found_space; + char *text; + size_t len; + + MLGetText(p, &text, &len); + if (text == NULL) return; + + if (li->formatted) HTMLPreformatted(li, env, text, len); + else + { + for (lastcp = text + len, cp = text; cp < lastcp; ) + { + for (found_space = false; cp < lastcp && isspace8(*cp); cp++) + { + found_space = true; + } + + if (found_space) + { + HTMLEnvAddBox(li, env, HTMLCreateTextBox(li, env, NULL, 0)); + } + if (cp == lastcp) break; + + for (s = cp; cp < lastcp && !isspace8(*cp); cp++) + ; + + HTMLEnvAddBox(li, env, HTMLCreateTextBox(li, env, s, cp - s)); + } + } + + return; +} + +void +HTMLPreBegin(li, env, p) +HTMLInfo li; +HTMLEnv env; +MLElement p; +{ + HTMLSetFontFixed(env->fi); + li->formatted = true; /* would anyone actually nest PREs? - djhjr */ + return; +} + +void +HTMLPreEnd(li, env, p) +HTMLInfo li; +HTMLEnv env; +MLElement p; +{ + li->formatted = false; /* nah... - djhjr */ + return; +} + +void +HTMLPlainEnd(li, env, p) +HTMLInfo li; +HTMLEnv env; +MLElement p; +{ + return; +} + +void +HTMLPlainBegin(li, env, p) +HTMLInfo li; +HTMLEnv env; +MLElement p; +{ + HTMLSetFontFixed(env->fi); + return; +} + +void +HTMLNoFrameBegin(li, env, p) +HTMLInfo li; +HTMLEnv env; +MLElement p; +{ + return; +} + +/* + * HTMLNoFrameEnd + */ +void +HTMLNoFrameEnd(li, env, p) +HTMLInfo li; +HTMLEnv env; +MLElement p; +{ + return; +} + +/* + * HTMLGetText + */ +char * +HTMLGetEnvText(mp, env) +MemPool mp; +HTMLEnv env; +{ + HTMLObject c, b, e; + char *text, *str; + size_t tlen = 0; + size_t len; + GList list = env->slist; + + if (GListEmpty(list)) return(NULL); + + e = (HTMLObject)GListGetTail(list); + for (b = c = (HTMLObject)GListGetHead(list); c != NULL; + c = (HTMLObject)GListGetNext(list)) + { + if (c != b && c != e) + { + if (c->type != HTML_ENV) + { + MLGetText(c->o.p, &str, &len); + tlen += len; + } + } + } + + text = MPGet(mp, tlen + 1); + + tlen = 0; + for (c = (HTMLObject)GListGetHead(list); c != NULL; + c = (HTMLObject)GListGetNext(list)) + { + if (c != b && c != e) + { + if (c->type != HTML_ENV) + { + MLGetText(c->o.p, &str, &len); + strncpy(text + tlen, str, len); + tlen += len; + } + } + } + text[tlen] = '\0'; + + return(text); +} + +/* + * HTMLAddLineBreak + */ +void +HTMLAddLineBreak(li, env) +HTMLInfo li; +HTMLEnv env; +{ + HTMLBox box; + + box = HTMLCreateBox(li, env); + HTMLSetB(box, BOX_LINEBREAK); + HTMLEnvAddBox(li, env, box); + + return; +} + +/* + * HTMLAddBlankLine + */ +void +HTMLAddBlankLine(li, env) +HTMLInfo li; +HTMLEnv env; +{ + HTMLBox box; + + HTMLAddLineBreak(li, env); + + box = HTMLCreateBox(li, env); + box->height = 6; + box->width = 0; /* really really want this to be zero */ + HTMLEnvAddBox(li, env, box); + + HTMLAddLineBreak(li, env); + + return; +} + +/* + * HTMLStringSpacify + * + * Turns all whitespace to spaces + */ +void +HTMLStringSpacify(s, len) +char *s; +size_t len; +{ + char *cp, *lastcp = s + len; + + for (cp = s; cp < lastcp; cp++) + { + if (isspace8(*cp)) *cp = ' '; + } + + return; +} diff --git a/image/Imakefile b/image/Imakefile new file mode 100644 index 0000000..07b485a --- /dev/null +++ b/image/Imakefile @@ -0,0 +1,28 @@ +#include <../Common.tmpl> + +#ifdef Use_JPEG +JPEG_INCLUDES = $(JPEGINCLUDE) +JPEG_DEFINES = -DHAVE_JPEG +JPEG_OBJS = jpeg.o +JPEG_SRCS = jpeg.c +#endif + +#ifdef Use_PNG +PNG_INCLUDES = $(PNGINCLUDE) +PNG_DEFINES = -DHAVE_PNG +PNG_OBJS = png.o +PNG_SRCS = png.c +#endif + +OBJS = gif.o xbm.o pnm.o new.o dispdither.o halftone.o \ + colorcube.o xcolorcube.o image.o $(JPEG_OBJS) $(PNG_OBJS) + +SRCS = gif.c xbm.c pnm.c new.c dispdither.c halftone.c \ + colorcube.c xcolorcube.c image.c $(JPEG_SRCS) $(PNG_SRCS) + +EXTRA_INCLUDES = -I../port -I../common -I../chimera $(JPEG_INCLUDES) $(PNG_INCLUDES) +EXTRA_DEFINES = $(XRELEASE) $(CHIMERA_DEFINES) $(JPEG_DEFINES) $(PNG_DEFINES) + +NormalLibraryTarget(ximage, $(OBJS)) + +DependTarget() diff --git a/image/colorcube.c b/image/colorcube.c new file mode 100644 index 0000000..1d518ec --- /dev/null +++ b/image/colorcube.c @@ -0,0 +1,562 @@ +/* colorcube.c + * + * (c) 1995 Erik Corry. ehcorry@inet.uni-c.dk erik@kroete2.freinet.de + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* + * This contains routines which take a general description of which + * colors we have managed to allocate from the device, and produces + * tables that can be used to convert various input formats to use + * these colors. The dither algorithm used (where necessary) is a + * dispersed table dither up to 4x4 in size, from an automatically + * generated table. + * + * The routines here are not necessarily fast (some of them are very + * slow indeed), but they generate tables that can be used in fast code. + * + * Error diffusion dither is not used for three reasons: + * + * o It is somewhat slower than table dither. + * o You cannot dither an interleaved image properly with it. + * o You cannot alter a small part of an image and then just redither that + * without the edges showing. + * o It is harder to program. + * o You shouldn't be trying to view jpegs on a 1-bit screen anyway. :-) + */ + +#include "port_before.h" + +#include + +#include "port_after.h" + +#include "imagep.h" +#include "colorcube.h" + +/* + * Create a matrix of the type: + * + * (1 3) + * (4 2) + * + * but of the required size, for a dispersed dither. + */ + +static void +lf_create_dither_matrix( + int matrix[4][4], + int tr, + int tc) +{ + int c,r; + bool swapped = false; + + /* Make sure the column count is <= row count */ + + if (tc > tr) + { + int t = tc; + tc = tr; + tr = t; + swapped = true; + } + + /* Seed the matrix */ + + matrix[0][0] = 1; + + /* Bring matrix up to size */ + + for (c = 1, r = 2; r <= tr; r *= 2) + { + int x,y; + int r2 = r/2; + int c2 = c; + for (y = 0; y < r2; y++) + { + for (x = 0; x < c; x++) + { + matrix[y + r2][x] = matrix[y][x] * 2 -1; + matrix[y][x] *= 2; + } + } + + c *=2; + if (c > tc) break; + + for (y = 0; y < r; y++) + { + for (x= 0; x < c2; x++) + { + matrix[(y + r2) % r][x + c2] = matrix[y][x] * 2 -1; + matrix[y][x] *= 2; + } + } + } + + /* Swap matrix width/height if necessary */ + + if (swapped) + { + for (r = 0; r < tr; r++) + { + for (c = 0; c < r; c++) + { + int t = matrix[r][c]; + matrix[r][c] = matrix[c][r]; + matrix[c][r] = t; + } + } + } +} + +static bool +lf_power_of_2(int t) +{ + if (t == (t&1)) return true; + return lf_power_of_2(t >> 1); +} + +static void +lf_calculate_dither_table( + unsigned int *brightnesses, + int value_count, + double max, + int total_columns, + int matrix[8], + unsigned int answer_pixel_values[8][256], + unsigned long pixel_values[256]) + { + int i, c, j; + double brightness_factor = 255.0 / + (brightnesses[value_count - 1] - + brightnesses[0]); + + for (c = 0; c < total_columns; c++) + { + for (i = 1; i < value_count; i++) + { + int l = brightnesses[i-1]; + int u = brightnesses[i]; + + l -= brightnesses[0]; + u -= brightnesses[0]; + l = (int)((double)l * brightness_factor + 0.001); + u = (int)((double)u * brightness_factor + 0.001); + + if(u != l) for (j = l; j <= u; j++) + { + if (((double)(j-l) / (double)(u-l)) <= + ((double)(matrix[c]-1) / max)) + answer_pixel_values[c][j] = pixel_values[i-1]; + else + answer_pixel_values[c][j] = pixel_values[i]; + /* + * Special hack to avoid spotty saturated colors + */ + if(u == 255 && j == 255) + answer_pixel_values[c][j] = pixel_values[i]; + } + } + } + for (c = total_columns; c < 4; c++) + { + for (i = 0; i < 256; i++) + { + answer_pixel_values[c][i] = answer_pixel_values[c % total_columns][i]; + } + } + } + +static bool +lf_check_rows_columns(int total_rows, int total_columns) +{ + /* Check inputs are reasonable */ + + if (total_rows > 4 || + total_columns > 4 || + total_rows < 1 || + total_columns < 1 || + !lf_power_of_2(total_rows) || + !lf_power_of_2(total_columns)) + { + return false; + } + + if (total_rows / total_columns > 2) return false; + if (total_columns / total_rows > 2) return false; + + return true; +} + +cct_true_8_dither_table +ccf_create_true_8_dither_table( + int row, + int total_rows, + int total_columns, + cct_cube cube) +{ + cct_true_8_dither_table answer; + int matrix[4][4]; + int i; + int rgb; + double max; + + if (!lf_check_rows_columns(total_rows, total_columns)) + return 0; + for (rgb = 0; rgb < 3; rgb++) + { + /* relies on union fields coinciding */ + if (cube->u.mapping.value_count[rgb] < 2) return 0; + } + + lf_create_dither_matrix(matrix, total_rows, total_columns); + + max = total_rows * total_columns - 1; + + answer = (cct_true_8_dither_table) + calloc(1, sizeof(struct ccs_true_8_dither_table)); + + answer->column_count = total_columns; + + answer->mapping_used = (cube->cube_type == cube_mapping); + if (answer->mapping_used) + { + for (i = 0; i < 256; i++) + answer->mapping[i] = cube->u.mapping.mapping[i]; + } + + for (rgb = 0; rgb < 3; rgb++) + { + lf_calculate_dither_table( + cube->u.mapping.brightnesses[rgb], + cube->u.mapping.value_count[rgb], + max, + total_columns, + matrix[row], + answer->pixel_values[rgb], + cube->u.mapping.pixel_values[rgb]); + } + + return answer; +} + +/* + * If a color is exactly allocated on the screen, outside of the + * color cube, there is no need to dither that color, it can be + * displayed directly. This routine amends the tables accordingly + */ + +void +ccf_set_specially_allocated( + cct_8_8_dither_table table, + unsigned char entry, + unsigned int pixel) +{ + int c; + for(c = 0; c < 4; c++) + { + table->pixel_values[c][entry] = pixel; + } +} + +cct_8_8_dither_table +ccf_create_gray_dither_table( + int row, + int total_rows, + int total_columns, + cct_cube cube) +{ + cct_8_8_dither_table answer; + int matrix[4][4]; + double max; + + if(!lf_check_rows_columns(total_rows, total_columns)) return 0; + + if (cube->u.grayscale.value_count < 2) return 0; + + lf_create_dither_matrix(matrix, total_rows, total_columns); + + max = total_rows * total_columns - 1; + + answer = (cct_8_8_dither_table) + calloc(1, sizeof(struct ccs_8_8_dither_table)); + + answer->column_count = total_columns; + + lf_calculate_dither_table( + cube->u.grayscale.brightnesses, + cube->u.grayscale.value_count, + max, + total_columns, + matrix[row], + answer->pixel_values, + cube->u.grayscale.pixel_values); + + return answer; +} + +/* + * For 8-bit palette input we do not need to convert to true-color + * before dithering. We simply make a new table that takes palette + * entries as inputs + */ + +cct_8_8_dither_table +ccf_true_8_to_8_8_dither_table( + cct_true_8_dither_table true_8, + int map_size, + unsigned char *colormaps[3]) +{ + cct_8_8_dither_table answer; + int c,i,rgb; + + answer = + (cct_8_8_dither_table) calloc(1, sizeof(struct ccs_8_8_dither_table)); + + answer->column_count = true_8->column_count; + + for (c = 0; c < 4; c++) + { + for (i = 0; i < map_size; i++) + { + for (rgb = 0; rgb < 3; rgb++) + { + answer->pixel_values[c][i] += + true_8->pixel_values[rgb][c][colormaps[rgb][i]]; + } + if (true_8->mapping_used) + answer->pixel_values[c][i] = + true_8->mapping[answer->pixel_values[c][i]]; + } + } + return answer; +} + + +/* + * How many 1 bits are there in the longword. + * Low performance, do not call often. + */ +static int +lf_number_of_bits_set(unsigned long a) +{ + if (!a) return 0; + if (a & 1) return 1 + lf_number_of_bits_set(a >> 1); + return(lf_number_of_bits_set(a >> 1)); +} + +/* + * Shift the 0s in the least significant end out of the longword. + * Low performance, do not call often. + */ +static unsigned long +lf_shifted_down(unsigned long a) +{ + if (!a) return 0ul; + if (a & 1) return a; + return lf_shifted_down(a >> 1); +} + +/* + * How many 0 bits are there at most significant end of longword. + * Low performance, do not call often. + */ +static int +lf_free_bits_at_top(unsigned long a) +{ + /* assume char is 8 bits */ + if (!a) return sizeof(unsigned long) * 8; + /* assume twos complement */ + if (((long)a) < 0l) return 0; + return 1 + lf_free_bits_at_top(a << 1); +} + +/* + * How many 0 bits are there at least significant end of longword. + * Low performance, do not call often. + */ +static int +lf_free_bits_at_bottom(unsigned long a) +{ + /* assume char is 8 bits */ + if (!a) return sizeof(unsigned long) * 8; + if (((long)a) & 1l) return 0; + return 1 + lf_free_bits_at_bottom(a >> 1); +} + + +cct_true_true_conversion_table +ccf_create_true_true_conversion_table(cct_cube cube) +{ + cct_true_true_conversion_table answer; + int i,rgb; + + answer = (cct_true_true_conversion_table) + calloc(1, sizeof(struct ccs_true_true_conversion_table)); + + for (rgb = 0; rgb < 3; rgb++) + { + int bits_set = lf_number_of_bits_set(cube->u.true_color.color_mask[rgb]); + int free_bits_at_bottom = + lf_free_bits_at_bottom(cube->u.true_color.color_mask[rgb]); + for (i = 0; i < 256; i++) + { + answer->pixel_values[rgb][i] = (i >> (8 - bits_set)) << free_bits_at_bottom; + if (rgb == 0) + answer->pixel_values[rgb][i] += cube->u.true_color.color_base; + } + } + return answer; +} + +/* + * For 8-bit palette input we do not need to convert to true-color + * one component at a time. We can do it in one lookup. This generates + * the table. + */ + +cct_8_true_conversion_table +ccf_true_true_to_8_true_conversion_table( + cct_true_true_conversion_table true_true, + int map_size, + unsigned char *colormaps[3]) +{ + cct_8_true_conversion_table answer; + int i, rgb; + + answer = (cct_8_true_conversion_table) + calloc(1, sizeof(struct ccs_8_true_conversion_table)); + + for (i = 0; i < map_size; i++) + { + for (rgb = 0; rgb < 3; rgb ++) + { + answer->pixel_values[i] += + true_true->pixel_values[rgb][colormaps[rgb][i]]; + } + } + return answer; +} + +/* + * Create gray table. This is assuming we have enough entries in the + * colormap that we do not need to dither, but can simply remap. This + * is about 64 entries, 32 at a push. + */ + +cct_8_true_conversion_table +ccf_create_gray_conversion_table(cct_cube cube) +{ + cct_8_true_conversion_table answer; + int i; + + answer = (cct_8_true_conversion_table) + calloc(1, sizeof(struct ccs_8_true_conversion_table)); + + for (i = 0; i < 256; i++) + { + int j; + int min_dist = 400000; /* big number ! */ + int min_entry; + for (j = 0; j < cube->u.grayscale.value_count; j++) + { + int dist; + dist = i - cube->u.grayscale.brightnesses[j]; + if (dist < 0) dist = -dist; + if (dist < min_dist) + { + min_dist = dist; + min_entry = j; + } + } + answer->pixel_values[i] = cube->u.grayscale.pixel_values[min_entry]; + } + return answer; +} + +/* + * If we are converting 8-bit palette to grayscale, we do not + * need to convert every pixel to gray, we can simply integrate + * the color map into the dither table + */ + +cct_8_8_dither_table +ccf_gray_to_gray_dither_convert( + cct_8_8_dither_table table, + int map_size, + unsigned char *colormaps[3]) +{ + int i; + int c; + cct_8_8_dither_table answer; + + answer = (cct_8_8_dither_table)calloc(1, sizeof(struct ccs_8_8_dither_table)); + + answer->column_count = table->column_count; + + for (i = 0; i < map_size; i++) + { + unsigned int bright = + RedIntensity[colormaps[0][i]] + + GreenIntensity[colormaps[1][i]] + + BlueIntensity[colormaps[2][i]]; + + bright >>= 8; + + for (c = 0; c < 4; c++) + { + answer->pixel_values[c][i] = table->pixel_values[c][bright]; + } + } + + return answer; +} + +/* + * If we are converting 8-bit palette to grayscale, we do not + * need to convert every pixel to gray, we can simply integrate + * the color map into the conversion table + */ + +cct_8_true_conversion_table +ccf_gray_to_gray_conversion_convert( + cct_8_true_conversion_table table, + int map_size, + unsigned char *colormaps[3]) +{ + int i; + cct_8_true_conversion_table answer; + + answer = (cct_8_true_conversion_table) + calloc(1, sizeof(struct ccs_8_true_conversion_table)); + + for (i = 0; i < map_size; i++) + { + unsigned int bright = + RedIntensity[colormaps[0][i]] + + GreenIntensity[colormaps[1][i]] + + BlueIntensity[colormaps[2][i]]; + + bright >>= 8; + + answer->pixel_values[i] = table->pixel_values[bright]; + } + + return answer; +} + diff --git a/image/colorcube.h b/image/colorcube.h new file mode 100644 index 0000000..7bfb26d --- /dev/null +++ b/image/colorcube.h @@ -0,0 +1,218 @@ +/* colorcube.h + * + * (c) 1995 Erik Corry. ehcorry@inet.uni-c.dk erik@kroete2.freinet.de + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* + * This contains routines which take a general description of which + * colors we have managed to allocate from the device, and produces + * tables that can be used to convert various input formats to use + * these colors. The dither algorithm used (where necessary) is a + * dispersed table dither up to 8x8 in size, from an automatically + * generated table. + * + * The routines here are not necessarily fast (some of them are very + * slow indeed), but they generate tables that can be used in fast code. + * + */ + +#ifndef COLORCUBE_H_INCLUDED +#define COLORCUBE_H_INCLUDED 1 + +typedef struct ccs_cube *cct_cube; +typedef union ccu_dither_table cct_dither_table; +typedef struct ccs_true_8_dither_table *cct_true_8_dither_table; +typedef struct ccs_8_8_dither_table *cct_8_8_dither_table; +typedef struct ccs_8_true_conversion_table *cct_8_true_conversion_table; +typedef struct ccs_true_true_conversion_table *cct_true_true_conversion_table; + +union ccu_dither_table +{ + void *generic_dither_table; + cct_true_8_dither_table true_8_dither; + cct_8_8_dither_table eight_8_dither; + cct_8_true_conversion_table eight_true_conversion; + cct_true_true_conversion_table true_true_conversion; +}; + +typedef enum +{ + cube_true_color, + cube_mapping, + cube_no_mapping, + cube_grayscale +} cce_cube_type; + +/* + * General description of colors that have been allocated on output + * device. + * + * For a small rxbxg cuboid, mapping or no_mapping is used. + * value_count[0] is the number of red planes, etc. Brightness + * is measured 0-255. To get a pixel number, add the pixel + * values together for r, g and b and then use the mapping table if + * necessary. + * + * For true_color (incl. 'hicolor') no dithering is necessary. + * Just shift and mask. + * + * brightnesses should be sorted in ascending order! + * + */ + +struct ccs_cube +{ + union + { + struct + { + unsigned long pixel_values[3][256]; + unsigned int brightnesses[3][256]; + int value_count[3]; + } no_mapping; + struct + { + unsigned long pixel_values[3][256]; + unsigned int brightnesses[3][256]; + int value_count[3]; + unsigned int mapping[256]; + } mapping; + struct + { + unsigned int color_mask[3]; + unsigned int color_base; + } true_color; + struct + { + unsigned long pixel_values[256]; + unsigned int brightnesses[256]; + int value_count; + } grayscale; + } u; + cce_cube_type cube_type; +}; + +struct ccs_special_color +{ + unsigned long pixel; + unsigned char brightnesses[3]; +}; + +typedef struct ccs_special_color *cct_special_color; + +/* + * Table for dithering from true-color to a colorcube allocated on + * an 4-16 bit pseudocolor screen. This table is only good for one + * row of a dither table, (which could be 2x2 up to 4x4). If the + * table has less than 4 columns, the first entries are duplicated + * so that 4 can always be assumed when using the table for simplicity. + * + * Since the mapping table has only 256 entries it can only be used on + * screens with 8 bits per pixel or less. + */ +struct ccs_true_8_dither_table +{ + unsigned int mapping[256]; + unsigned int pixel_values[3][4][256]; + int column_count; /* 2-8 */ + bool mapping_used; +}; + +/* + * Table for dithering from an 8-bit palette image to a color image + * based on a fixed allocation of colors on the screen. + * + * Also used as a: + * Table for dithering from gray 8-bit pixels to less gray levels. + * Works right down to only 2 different colors i.e. black and white + * See above wrt. column_count + */ +struct ccs_8_8_dither_table +{ + unsigned int pixel_values[4][256]; + int column_count; /* 2-8 */ +}; + +/* + * Table for converting 8-bit colormap pixels to true color (or + * hi-color) pixels on screen. + */ +struct ccs_8_true_conversion_table +{ + unsigned int pixel_values[256]; +}; + +/* + * Table for converting a true-color 24-bit image to a true-color + * or hi-color screen with a different information layout. + */ +struct ccs_true_true_conversion_table +{ + unsigned int pixel_values[3][256]; +}; + +cct_true_8_dither_table +ccf_create_true_8_dither_table( + int row, + int total_rows, + int total_columns, + cct_cube cube); + +cct_8_8_dither_table +ccf_create_gray_dither_table( + int row, + int total_rows, + int total_columns, + cct_cube cube); + +cct_8_8_dither_table +ccf_true_8_to_8_8_dither_table( + cct_true_8_dither_table true_8, + int map_size, + unsigned char *colormaps[3]); + +cct_true_true_conversion_table +ccf_create_true_true_conversion_table(cct_cube cube); + +cct_8_true_conversion_table +ccf_true_true_to_8_true_conversion_table( + cct_true_true_conversion_table true_true, + int map_size, + unsigned char *colormaps[3]); + +cct_8_true_conversion_table +ccf_create_gray_conversion_table(cct_cube cube); + +cct_8_8_dither_table +ccf_gray_to_gray_dither_convert( + cct_8_8_dither_table table, + int map_size, + unsigned char *colormaps[3]); + +cct_8_true_conversion_table +ccf_gray_to_gray_conversion_convert( + cct_8_true_conversion_table table, + int map_size, + unsigned char *colormaps[3]); + +void +ccf_set_specially_allocated( + cct_8_8_dither_table table, + unsigned char entry, + unsigned int pixel); + +#endif /* COLORCUBE_H_INCLUDED */ diff --git a/image/dispdither.c b/image/dispdither.c new file mode 100644 index 0000000..83df627 --- /dev/null +++ b/image/dispdither.c @@ -0,0 +1,1040 @@ +/* dispdither.c + * + * (c) 1995 Erik Corry. ehcorry@inet.uni-c.dk erik@kroete2.freinet.de + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* + * This contains routines which dither or convert image data from 8 or + * 24 bit input data to 1/2/4/8/16/24/32 bit output, possibly dithered. + * + * Error diffusion dither is not used for three reasons: + * + * o It is somewhat slower than table dither. + * o You cannot dither an interleaved image properly with it. + * o You cannot alter a small part of an image and then just redither that + * without the edges showing. + * o It is harder to program. + * o You shouldn't be trying to view jpegs on a 1-bit screen anyway. :-) + */ + +#include "port_before.h" + +#include + +#ifdef HAVE_STRING_H +#include +#endif + +#include "port_after.h" + +#include "imagep.h" +#include "colorcube.h" +#include "dispdither.h" +#include "image_endian.h" + +/* + * The dither routines all end up in a tight loop that has no: + * o if's (pipeline catastrophe ) + * o floating point (SX, NexGen, etc. ) + * o division (system trap on Alpha ) + * o unnecessary variables (register spills on x86s) + * and which isn't too long, so it can be superoptimised. + */ + + +static void +lf_color_dither_line_24_4_no_mapping( + cct_true_8_dither_table table, + unsigned char *input, + unsigned char *output, + int pixel_count) +{ + int x, xx; + for(xx = 0, x = pixel_count >> 1; x; x--, xx += 2) + { + unsigned char t; + xx = xx & 3; +# ifdef CHIMERA_LITTLE_ENDIAN + t = table->pixel_values[0][xx+1][input[3]]; + t += table->pixel_values[1][xx+1][input[4]]; + t = (t + table->pixel_values[2][xx+1][input[5]]) << 4; + t += table->pixel_values[0][xx][input[0]]; + t += table->pixel_values[1][xx][input[1]]; + t += table->pixel_values[2][xx][input[2]]; +# else + t = table->pixel_values[0][xx][input[0]]; + t += table->pixel_values[1][xx][input[1]]; + t = (t + table->pixel_values[2][xx][input[2]]) << 4; + t += table->pixel_values[0][xx+1][input[3]]; + t += table->pixel_values[1][xx+1][input[4]]; + t += table->pixel_values[2][xx+1][input[5]]; +# endif + *output++ = t; + input += 6; + } +} + +static void +lf_color_dither_line_24_4_mapping( + cct_true_8_dither_table table, + unsigned char *input, + unsigned char *output, + int pixel_count) +{ + int x, xx; + for(xx = 0, x = pixel_count >> 1; x; x--, xx += 2) + { + unsigned char t, tt; + xx = xx & 3; +# ifdef CHIMERA_LITTLE_ENDIAN + t = table->pixel_values[0][xx][input[0]]; + t += table->pixel_values[1][xx][input[1]]; + t += table->pixel_values[2][xx][input[2]]; + t = table->mapping[t]; + tt = table->pixel_values[0][xx+1][input[3]]; + tt += table->pixel_values[1][xx+1][input[4]]; + tt += table->pixel_values[2][xx+1][input[5]]; + tt = table->mapping[tt] << 4; +# else + t = table->pixel_values[0][xx][input[0]]; + t += table->pixel_values[1][xx][input[1]]; + t += table->pixel_values[2][xx][input[2]]; + t = table->mapping[t] << 4; + tt = table->pixel_values[0][xx+1][input[3]]; + tt += table->pixel_values[1][xx+1][input[4]]; + tt += table->pixel_values[2][xx+1][input[5]]; + tt = table->mapping[tt]; +# endif + *output++ = t | tt; + input += 6; + } +} + +static void +lf_color_dither_line_24_4( + cct_true_8_dither_table table, + unsigned char *input, + unsigned char *output, + int pixel_count) +{ + if(table->mapping_used) + lf_color_dither_line_24_4_mapping(table, input, output, pixel_count); + else + lf_color_dither_line_24_4_no_mapping(table, input, output, pixel_count); + return; +} + +static void +lf_color_dither_line_24_8_no_mapping( + cct_true_8_dither_table table, + unsigned char *input, + unsigned char *output, + int pixel_count) +{ + int x, xx; + for(xx = 0, x = pixel_count; x; x--, xx++) + { + xx = xx & 3; + *output++ = table->pixel_values[0][xx][input[0]] + + table->pixel_values[1][xx][input[1]] + + table->pixel_values[2][xx][input[2]]; + input += 3; + } +} + +static void +lf_color_dither_line_24_8_mapping( + cct_true_8_dither_table table, + unsigned char *input, + unsigned char *output, + int pixel_count) +{ + int x, xx; + for(xx = 0, x = pixel_count; x; x--, xx++) + { + int t; + xx = xx & 3; + t = table->pixel_values[0][xx][input[0]] + + table->pixel_values[1][xx][input[1]] + + table->pixel_values[2][xx][input[2]]; + *output++ = table->mapping[t]; + input += 3; + } +} + +static void +lf_color_dither_line_24_8( + cct_true_8_dither_table table, + unsigned char *input, + unsigned char *output, + int pixel_count) +{ + if(table->mapping_used) + lf_color_dither_line_24_8_mapping(table, input, output, pixel_count); + else + lf_color_dither_line_24_8_no_mapping(table, input, output, pixel_count); + return; +} + +/* + * Ignores mapping_used + */ + +static void +lf_color_dither_line_24_16( + cct_true_8_dither_table table, + unsigned char *input, + unsigned short *output, + int pixel_count) +{ + int x, xx; + for(xx = 0, x = pixel_count; x; x--, xx++) + { + xx = xx & 3; + *output++ = table->pixel_values[0][xx][input[0]] + + table->pixel_values[1][xx][input[1]] + + table->pixel_values[2][xx][input[2]]; + input += 3; + } +} + +/* + * Works for 4,8,16 bits. You need at least 3 bits for 3 colors. Above 16 + * there's never any point in dithering. People with 13 bit screens can + * mail me the patches. + */ + +void +ddf_color_dither_line_24( + cct_dither_table table, + unsigned char *input, + unsigned char *output, + int output_bits_per_pixel, + int pixel_count) +{ + switch(output_bits_per_pixel) + { + case 4: + if(pixel_count & 1) pixel_count++; + lf_color_dither_line_24_4(table.true_8_dither, input, + output, pixel_count); + break; + case 8: + lf_color_dither_line_24_8(table.true_8_dither, input, + output, pixel_count); + break; + case 16: + lf_color_dither_line_24_16(table.true_8_dither, input, + (unsigned short *)output, pixel_count); + break; + default: + /* see this in the debugger :-) */ + sprintf(output, "ERROR: color dither to %d bits attempted", output_bits_per_pixel); + break; + } +} + +/***********************************************************************/ + +static void +lf_gray_dither_24_1( + cct_8_8_dither_table table, + unsigned char *input, + unsigned char *output, + int pixel_count) +{ + int x; + for(x = pixel_count >> 3; x; x--) + { + unsigned int t; + unsigned int b; +# ifdef CHIMERA_LITTLE_ENDIAN + b = RedIntensity[input[0]] + GreenIntensity[input[1]] + BlueIntensity[input[2]]; + b >>= 8; + t = table->pixel_values[0][b]; + b = RedIntensity[input[3]] + GreenIntensity[input[4]] + BlueIntensity[input[5]]; + b >>= 8; + t |= table->pixel_values[1][b] << 1; + b = RedIntensity[input[6]] + GreenIntensity[input[7]] + BlueIntensity[input[8]]; + b >>= 8; + t |= table->pixel_values[2][b] << 2; + b = RedIntensity[input[9]] + GreenIntensity[input[10]] + BlueIntensity[input[11]]; + b >>= 8; + t |= table->pixel_values[3][b] << 3; + b = RedIntensity[input[12]] + GreenIntensity[input[13]] + BlueIntensity[input[14]]; + b >>= 8; + t |= table->pixel_values[0][b] << 4; + b = RedIntensity[input[15]] + GreenIntensity[input[16]] + BlueIntensity[input[17]]; + b >>= 8; + t |= table->pixel_values[1][b] << 5; + b = RedIntensity[input[18]] + GreenIntensity[input[19]] + BlueIntensity[input[20]]; + b >>= 8; + t |= table->pixel_values[2][b] << 6; + b = RedIntensity[input[21]] + GreenIntensity[input[22]] + BlueIntensity[input[23]]; + b >>= 8; + t |= table->pixel_values[3][b] << 7; +# else + b = RedIntensity[input[0]] + GreenIntensity[input[1]] + BlueIntensity[input[2]]; + b >>= 8; + t = table->pixel_values[0][b] << 7; + b = RedIntensity[input[3]] + GreenIntensity[input[4]] + BlueIntensity[input[5]]; + b >>= 8; + t |= table->pixel_values[1][b] << 6; + b = RedIntensity[input[6]] + GreenIntensity[input[7]] + BlueIntensity[input[8]]; + b >>= 8; + t |= table->pixel_values[2][b] << 5; + b = RedIntensity[input[9]] + GreenIntensity[input[10]] + BlueIntensity[input[11]]; + b >>= 8; + t |= table->pixel_values[3][b] << 4; + b = RedIntensity[input[12]] + GreenIntensity[input[13]] + BlueIntensity[input[14]]; + b >>= 8; + t |= table->pixel_values[0][b] << 3; + b = RedIntensity[input[15]] + GreenIntensity[input[16]] + BlueIntensity[input[17]]; + b >>= 8; + t |= table->pixel_values[1][b] << 2; + b = RedIntensity[input[18]] + GreenIntensity[input[19]] + BlueIntensity[input[20]]; + b >>= 8; + t |= table->pixel_values[2][b] << 1; + b = RedIntensity[input[21]] + GreenIntensity[input[22]] + BlueIntensity[input[23]]; + b >>= 8; + t |= table->pixel_values[3][b]; +# endif + *output++ = t; + input += 24; + } +} + +static void +lf_gray_dither_24_2( + cct_8_8_dither_table table, + unsigned char *input, + unsigned char *output, + int pixel_count) +{ + int x; + for(x = pixel_count >> 2; x; x--) + { + unsigned int t; + unsigned int b; +# ifdef CHIMERA_LITTLE_ENDIAN + b = RedIntensity[input[0]] + GreenIntensity[input[1]] + BlueIntensity[input[2]]; + b >>= 8; + t = table->pixel_values[0][b]; + b = RedIntensity[input[3]] + GreenIntensity[input[4]] + BlueIntensity[input[5]]; + b >>= 8; + t |= table->pixel_values[1][b] << 2; + b = RedIntensity[input[6]] + GreenIntensity[input[7]] + BlueIntensity[input[8]]; + b >>= 8; + t |= table->pixel_values[2][b] << 4; + b = RedIntensity[input[9]] + GreenIntensity[input[10]] + BlueIntensity[input[11]]; + b >>= 8; + t |= table->pixel_values[3][b] << 6; +# else + b = RedIntensity[input[0]] + GreenIntensity[input[1]] + BlueIntensity[input[2]]; + b >>= 8; + t = table->pixel_values[0][b] << 6; + b = RedIntensity[input[3]] + GreenIntensity[input[4]] + BlueIntensity[input[5]]; + b >>= 8; + t |= table->pixel_values[1][b] << 4; + b = RedIntensity[input[6]] + GreenIntensity[input[7]] + BlueIntensity[input[8]]; + b >>= 8; + t |= table->pixel_values[2][b] << 2; + b = RedIntensity[input[9]] + GreenIntensity[input[10]] + BlueIntensity[input[11]]; + b >>= 8; + t |= table->pixel_values[3][b]; +# endif + *output++ = t; + input += 12; + } +} + +static void +lf_gray_dither_24_4( + cct_8_8_dither_table table, + unsigned char *input, + unsigned char *output, + int pixel_count) +{ + int x; + int xx = 0; + for(x = pixel_count >> 1; x; x--, xx += 2) + { + unsigned int t; + unsigned int b; + xx &= 3; +# ifdef CHIMERA_LITTLE_ENDIAN + b = RedIntensity[input[0]] + GreenIntensity[input[1]] + BlueIntensity[input[2]]; + b >>= 8; + t = table->pixel_values[xx][b]; + b = RedIntensity[input[3]] + GreenIntensity[input[4]] + BlueIntensity[input[5]]; + b >>= 8; + t |= table->pixel_values[xx+1][b] << 4; +# else + b = RedIntensity[input[0]] + GreenIntensity[input[1]] + BlueIntensity[input[2]]; + b >>= 8; + t = table->pixel_values[xx][b] << 4; + b = RedIntensity[input[3]] + GreenIntensity[input[4]] + BlueIntensity[input[5]]; + b >>= 8; + t |= table->pixel_values[xx+1][b]; +# endif + *output++ = t; + input += 6; + } +} + +static void +lf_gray_dither_24_8( + cct_8_8_dither_table table, + unsigned char *input, + unsigned char *output, + int pixel_count) +{ + int x; + int xx = 0; + for(x = pixel_count; x; x--, xx++) + { + unsigned char t; + unsigned int b; + xx &= 3; + b = RedIntensity[input[0]] + GreenIntensity[input[1]] + BlueIntensity[input[2]]; + b >>= 8; + t = table->pixel_values[xx][b]; + *output++ = t; + input += 3; + } +} + +static void +lf_gray_dither_24_16( + cct_8_8_dither_table table, + unsigned char *input, + unsigned short *output, + int pixel_count) +{ + int x; + int xx = 0; + for(x = pixel_count >> 1; x; x--, xx++) + { + unsigned char t; + unsigned int b; + xx &= 3; + b = RedIntensity[input[0]] + GreenIntensity[input[1]] + BlueIntensity[input[2]]; + b >>= 8; + t = table->pixel_values[xx][b]; + *output++ = t; + input += 3; + } +} + +/* + * Takes 24 bit input and dithers to gray + */ + +void +ddf_gray_dither_line_24( + cct_dither_table table, + unsigned char *input, + unsigned char *output, + int output_bits_per_pixel, + int pixel_count) +{ + switch(output_bits_per_pixel) + { + case 1: + while(pixel_count & 7) pixel_count++; + lf_gray_dither_24_1(table.eight_8_dither, input, output, pixel_count); + break; + case 2: + while(pixel_count & 3) pixel_count++; + lf_gray_dither_24_2(table.eight_8_dither, input, output, pixel_count); + break; + case 4: + if(pixel_count & 1) pixel_count++; + lf_gray_dither_24_4(table.eight_8_dither, input, output, pixel_count); + break; + case 8: + lf_gray_dither_24_8(table.eight_8_dither, input, output, pixel_count); + break; + case 16: + lf_gray_dither_24_16(table.eight_8_dither, input, + (unsigned short *)output, pixel_count); + break; + default: + /* see this in the debugger :-) */ + sprintf(output, "ERROR: color dither to %d bits attempted", output_bits_per_pixel); + break; + } +} + + +/************************************************************************/ + + +static void +lf_dither_8_1( + cct_8_8_dither_table table, + unsigned char *input, + unsigned char *output, + int pixel_count) +{ + int x; + for(x = pixel_count >> 3; x; x--) + { + unsigned int t; + unsigned int b; +# ifdef CHIMERA_LITTLE_ENDIAN + b = input[0]; + t = table->pixel_values[0][b]; + b = input[1]; + t |= table->pixel_values[1][b] << 1; + b = input[2]; + t |= table->pixel_values[2][b] << 2; + b = input[3]; + t |= table->pixel_values[3][b] << 3; + b = input[4]; + t |= table->pixel_values[0][b] << 4; + b = input[5]; + t |= table->pixel_values[1][b] << 5; + b = input[6]; + t |= table->pixel_values[2][b] << 6; + b = input[7]; + t |= table->pixel_values[3][b] << 7; +# else + b = input[0]; + t = table->pixel_values[0][b] << 7; + b = input[1]; + t |= table->pixel_values[1][b] << 6; + b = input[2]; + t |= table->pixel_values[2][b] << 5; + b = input[3]; + t |= table->pixel_values[3][b] << 4; + b = input[4]; + t |= table->pixel_values[0][b] << 3; + b = input[5]; + t |= table->pixel_values[1][b] << 2; + b = input[6]; + t |= table->pixel_values[2][b] << 1; + b = input[7]; + t |= table->pixel_values[3][b]; +# endif + *output++ = t; + input += 8; + } +} + +static void +lf_dither_8_2( + cct_8_8_dither_table table, + unsigned char *input, + unsigned char *output, + int pixel_count) +{ + int x; + for(x = pixel_count >> 2; x; x--) + { + unsigned int t; + unsigned int b; +# ifdef CHIMERA_LITTLE_ENDIAN + b = input[0]; + t = table->pixel_values[0][b]; + b = input[1]; + t |= table->pixel_values[1][b] << 2; + b = input[2]; + t |= table->pixel_values[2][b] << 4; + b = input[3]; + t |= table->pixel_values[3][b] << 6; +# else + b = input[0]; + t = table->pixel_values[0][b] << 6; + b = input[1]; + t |= table->pixel_values[1][b] << 4; + b = input[2]; + t |= table->pixel_values[2][b] << 2; + b = input[3]; + t |= table->pixel_values[3][b]; +# endif + *output++ = t; + input += 4; + } +} + +static void +lf_dither_8_4( + cct_8_8_dither_table table, + unsigned char *input, + unsigned char *output, + int pixel_count) +{ + int x; + int xx = 0; + for(x = pixel_count >> 1; x; x--, xx += 2) + { + unsigned int t; + unsigned int b; + xx &= 3; +# ifdef CHIMERA_LITTLE_ENDIAN + b = input[0]; + t = table->pixel_values[xx][b]; + b = input[1]; + t |= table->pixel_values[xx+1][b] << 4; +# else + b = input[0]; + t = table->pixel_values[xx][b] << 4; + b = input[1]; + t |= table->pixel_values[xx+ 1][b]; +# endif + *output++ = t; + input += 2; + } +} + +/* + * This is it! This is where Mr. averages GIFs get dithered for his + * his 8-bit pseudocolor screen! + */ + +static void +lf_dither_8_8( + cct_8_8_dither_table table, + unsigned char *input, + unsigned char *output, + int pixel_count) +{ + int x; + int xx = 0; + + for(x = pixel_count; x; x--, xx++) + { + unsigned int t; + unsigned int b; + xx &= 3; + b = input[0]; + t = table->pixel_values[xx][b]; + *output++ = t; + input ++; + } +} + +static void +lf_dither_8_16( + cct_8_8_dither_table table, + unsigned char *input, + unsigned short *output, + int pixel_count) +{ + int x; + int xx = 0; + for(x = pixel_count; x; x--, xx++) + { + unsigned int t; + unsigned int b; + xx &= 3; + b = input[0]; + t = table->pixel_values[xx][b]; + *output++ = t; + input++; + } +} + +/* + * takes 8 bit input and dithers + */ + +void +ddf_dither_line_8( + cct_dither_table table, + unsigned char *input, + unsigned char *output, + int output_bits_per_pixel, + int pixel_count) +{ + switch(output_bits_per_pixel) + { + case 1: + while(pixel_count & 7) pixel_count++; + lf_dither_8_1(table.eight_8_dither, input, output, pixel_count); + break; + case 2: + while(pixel_count & 3) pixel_count++; + lf_dither_8_2(table.eight_8_dither, input, output, pixel_count); + break; + case 4: + if(pixel_count & 1) pixel_count++; + lf_dither_8_4(table.eight_8_dither, input, output, pixel_count); + break; + case 8: + lf_dither_8_8(table.eight_8_dither, input, output, pixel_count); + break; + case 16: + lf_dither_8_16(table.eight_8_dither, input, (unsigned short *)output, pixel_count); + break; + default: + /* see this in the debugger :-) */ + sprintf(output, "ERROR: color dither to %d bits attempted", output_bits_per_pixel); + break; + } +} + + +/************************************************************************/ + +static void +lf_convert_8_4( + cct_8_true_conversion_table table, + unsigned char *input, + unsigned char *output, + int pixel_count) +{ + int x; + for(x = pixel_count >> 1; x; x--) + { + unsigned int b; +# ifdef CHIMERA_LITTLE_ENDIAN + b = table->pixel_values[input[0]]; + b |= table->pixel_values[input[1]] << 4; +# else + b = table->pixel_values[input[0]] << 4; + b |= table->pixel_values[input[1]]; +# endif + *output++ = b; + input++; + } +} + +static void +lf_convert_8_8( + cct_8_true_conversion_table table, + unsigned char *input, + unsigned char *output, + int pixel_count) +{ + int x; + for(x = pixel_count; x; x--) + { + output[0] = table->pixel_values[input[0]]; + output++; + input++; + } +} + +static void +lf_convert_8_16( + cct_8_true_conversion_table table, + unsigned char *input, + unsigned short *output, + int pixel_count) +{ + int x; + for(x = pixel_count; x; x--) + { + output[0] = table->pixel_values[input[0]]; + output++; + input++; + } +} + +static void +lf_convert_8_24( + cct_8_true_conversion_table table, + unsigned char *input, + unsigned char *output, + int pixel_count) +{ + int x; + for(x = pixel_count; x; x--) + { + unsigned int t = table->pixel_values[input[0]]; +# ifdef CHIMERA_LITTLE_ENDIAN + output[0] = t & 255; + output[1] = (t >> 8) & 255; + output[2] = (t >> 16) & 255; +# else + output[0] = (t >> 16) & 255; + output[1] = (t >> 8) & 255; + output[2] = t & 255; +# endif + output += 3; + input++; + } +} + + +static void +lf_convert_8_32( + cct_8_true_conversion_table table, + unsigned char *input, + unsigned int *output, + int pixel_count) +{ + int x; + for(x = pixel_count; x; x--) + { + output[0] = table->pixel_values[input[0]]; + output++; + input++; + } +} + +/* + * takes 8 bit input and converts + */ + +void +ddf_convert_line_8( + cct_dither_table table, + unsigned char *input, + unsigned char *output, + int output_bits_per_pixel, + int pixel_count) +{ + switch(output_bits_per_pixel) + { + case 4: + if(pixel_count & 1) pixel_count++; + lf_convert_8_4(table.eight_true_conversion, input, output, pixel_count); + break; + case 8: + lf_convert_8_8(table.eight_true_conversion, input, output, pixel_count); + break; + case 16: + lf_convert_8_16(table.eight_true_conversion, input, + (unsigned short *)output, pixel_count); + break; + case 24: + lf_convert_8_24(table.eight_true_conversion, input, output, pixel_count); + break; + case 32: + lf_convert_8_32(table.eight_true_conversion, input, + (unsigned int *)output, pixel_count); + break; + default: + /* see this in the debugger :-) */ + sprintf(output, "ERROR: color dither to %d bits attempted", output_bits_per_pixel); + break; + } +} + + +/************************************************************************/ + + +static void +lf_convert_24_16( + cct_true_true_conversion_table table, + unsigned char *input, + unsigned short *output, + int pixel_count) +{ + int x; + for(x = pixel_count; x; x--) + { + unsigned int o; + o = table->pixel_values[0][input[0]]; + o += table->pixel_values[1][input[1]]; + o += table->pixel_values[2][input[2]]; + *output++ = o; + input += 3; + } +} + +static void +lf_convert_24_32( + cct_true_true_conversion_table table, + unsigned char *input, + unsigned int *output, + int pixel_count) +{ + int x; + for(x = pixel_count; x; x--) + { + unsigned int o; + o = table->pixel_values[0][input[0]]; + o += table->pixel_values[1][input[1]]; + o += table->pixel_values[2][input[2]]; + *output++ = o; + input += 3; + } +} + +static void +lf_convert_24_24( + cct_true_true_conversion_table table, + unsigned char *input, + unsigned char *output, + int pixel_count) +{ + int x; + for(x = pixel_count; x; x--) + { + unsigned int t = table->pixel_values[0][input[0]]; + t += table->pixel_values[1][input[1]]; + t += table->pixel_values[2][input[2]]; +# ifdef CHIMERA_LITTLE_ENDIAN + output[0] = t & 255; + output[1] = (t >> 8) & 255; + output[2] = (t >> 16) & 255; +# else + output[0] = (t >> 16) & 255; + output[1] = (t >> 8) & 255; + output[2] = t & 255; +# endif + output += 3; + input += 3; + } +} + + +/* + * takes 24 bit input and converts + */ + +void +ddf_convert_line_24( + cct_dither_table table, + unsigned char *input, + unsigned char *output, + int output_bits_per_pixel, + int pixel_count) +{ + switch(output_bits_per_pixel) + { + case 16: + lf_convert_24_16(table.true_true_conversion, input, + (unsigned short *)output, pixel_count); + break; + case 24: + lf_convert_24_24(table.true_true_conversion, input, output, pixel_count); + break; + case 32: + lf_convert_24_32(table.true_true_conversion, input, + (unsigned int *)output, pixel_count); + break; + default: + /* see this in the debugger :-) */ + sprintf(output, "ERROR: color dither to %d bits attempted", output_bits_per_pixel); + break; + } +} + + +/************************************************************************/ + +static void +lf_gray_convert_24_4( + cct_8_true_conversion_table table, + unsigned char *input, + unsigned char *output, + int pixel_count) +{ + int x; + for(x = pixel_count >> 1; x; x--) + { + unsigned int b; +# ifdef CHIMERA_LITTLE_ENDIAN + b = table->pixel_values[(RedIntensity[input[0]] + + GreenIntensity[input[1]] + + BlueIntensity[input[2]]) >> 8]; + b |= table->pixel_values[(RedIntensity[input[3]] + + GreenIntensity[input[4]] + + BlueIntensity[input[5]]) >> 8] << 4; +# else + b = table->pixel_values[(RedIntensity[input[0]] + + GreenIntensity[input[1]] + + BlueIntensity[input[2]]) >> 8] << 4; + b |= table->pixel_values[(RedIntensity[input[3]] + + GreenIntensity[input[4]] + + BlueIntensity[input[5]]) >> 8]; +# endif + b >>= 8; + *output++ = b; + input+= 6; + } +} + +static void +lf_gray_convert_24_8( + cct_8_true_conversion_table table, + unsigned char *input, + unsigned char *output, + int pixel_count) +{ + int x; + for(x = pixel_count; x; x--) + { + unsigned int b = + RedIntensity[input[0]] + GreenIntensity[input[1]] + BlueIntensity[input[2]]; + b >>= 8; + *output++ = table->pixel_values[b]; + input+= 3; + } +} + + +static void +lf_gray_convert_24_16( + cct_8_true_conversion_table table, + unsigned char *input, + unsigned short *output, + int pixel_count) +{ + int x; + for(x = pixel_count; x; x--) + { + unsigned int b = + RedIntensity[input[0]] + GreenIntensity[input[1]] + BlueIntensity[input[2]]; + b >>= 8; + *output++ = table->pixel_values[b]; + input+= 3; + } +} +/* + * Takes 24 bit input and converts to gray + */ + +void +ddf_gray_convert_line_24( + cct_dither_table table, + unsigned char *input, + unsigned char *output, + int output_bits_per_pixel, + int pixel_count) +{ + switch(output_bits_per_pixel) + { + case 4: + if(pixel_count & 1) pixel_count++; + lf_gray_convert_24_4(table.eight_true_conversion, input, + output, pixel_count); + break; + case 8: + lf_gray_convert_24_8(table.eight_true_conversion, input, + output, pixel_count); + break; + case 16: + lf_gray_convert_24_16(table.eight_true_conversion, input, + (unsigned short *)output, pixel_count); + break; + default: + /* see this in the debugger :-) */ + sprintf(output, "ERROR: color dither to %d bits attempted", output_bits_per_pixel); + break; + } +} + + +/************************************************************************/ + diff --git a/image/dispdither.h b/image/dispdither.h new file mode 100644 index 0000000..79e2745 --- /dev/null +++ b/image/dispdither.h @@ -0,0 +1,101 @@ +/* dispdither.h + * + * (c) 1995 Erik Corry. ehcorry@inet.uni-c.dk erik@kroete2.freinet.de + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* + * This contains routines which dither or convert image data from 8 or + * 24 bit input data to 1/2/4/8/16/24/32 bit output, possibly dithered. + */ + +#ifndef DISPDITHER_H_INCLUDED +#define DISPDITHER_H_INCLUDED + +#include "colorcube.h" + +/* + * ddt_dither_fn is a pointer to a function returning void + */ +typedef void (*ddt_dither_fn)(); + +/* + * 4, 8, 16 obpp + */ +void +ddf_color_dither_line_24( + cct_dither_table table, + unsigned char *input, + unsigned char *output, + int output_bits_per_pixel, + int pixel_count); + +/* + * 1, 2, 4, 8, 16 obpp + */ +void +ddf_gray_dither_line_24( + cct_dither_table table, + unsigned char *input, + unsigned char *output, + int output_bits_per_pixel, + int pixel_count); + +/* + * 4, 8, 16 obpp + */ +void +ddf_gray_convert_line_24( + cct_dither_table table, + unsigned char *input, + unsigned char *output, + int output_bits_per_pixel, + int pixel_count); + +/* + * 1, 2, 4, 8, 16 obpp + */ +void +ddf_dither_line_8( + cct_dither_table table, + unsigned char *input, + unsigned char *output, + int output_bits_per_pixel, + int pixel_count); + +/* + * 4, 8, 16, 24, 32 obpp + */ +void +ddf_convert_line_8( + cct_dither_table table, + unsigned char *input, + unsigned char *output, + int output_bits_per_pixel, + int pixel_count); + +/* + * 16, 24, 32 obpp + */ +void +ddf_convert_line_24( + cct_dither_table table, + unsigned char *input, + unsigned char *output, + int output_bits_per_pixel, + int pixel_count); + +#endif /* DISPDITHER_H_INCLUDED */ diff --git a/image/gif.c b/image/gif.c new file mode 100644 index 0000000..bba7be0 --- /dev/null +++ b/image/gif.c @@ -0,0 +1,740 @@ +/* + * gif.c: + * + * Modified to be called so that GIFs can be decoded "on-the-fly". + * Sort of. Reparses lots of data it doesn't have to. + * John Kilburg + * + * adapted from code by kirk johnson (tuna@athena.mit.edu). most of this + * code is unchanged. -- jim frost 12.31.89 + * + * gifin.c + * kirk johnson + * november 1989 + * + * routines for reading GIF files + * + * Copyright 1989 Kirk L. Johnson + * + * Permission to use, copy, modify, distribute, and sell this + * software and its documentation for any purpose is hereby granted + * without fee, provided that the above copyright notice appear in + * all copies and that both that copyright notice and this + * permission notice appear in supporting documentation. The + * author makes no representations about the suitability of this + * software for any purpose. It is provided "as is" without express + * or implied warranty. + * + * THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, + * IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, INDIRECT + * OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM + * LOSS OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, + * NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN + * CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE. + * + */ +#include "port_before.h" + +#include + +#ifdef HAVE_STRING_H +#include +#endif + +#ifdef HAVE_STDLIB_H +#include +#endif + +#include "port_after.h" + +#include "common.h" + +#include "imagep.h" +#include "image_endian.h" +#include "image_format.h" +#include "gifp.h" + +/* start line for interlacing */ +static int interlace_start[4] = { 0, 4, 2, 1 }; + +/* rate at which we accelerate vertically */ +static int interlace_rate[4] = { 8, 8, 4, 2 }; + +/* how often to copy the line */ +static int interlace_copy[4] = { 8, 4, 2, 1 }; + +static int gs_read_image_header _ArgProto((gifState *)); +static int gs_read_sig _ArgProto((gifState *)); +static int gs_read_data_block _ArgProto((gifState *)); +static int gs_read _ArgProto((gifState *, byte **, int)); +static int gs_open_file _ArgProto((gifState *)); +static int gs_open_image _ArgProto((gifState *)); +static int gs_get_pixel _ArgProto((gifState *, int *)); +static int gs_init_decoder _ArgProto((gifState *)); + +/* + * read GIF data + */ +static int +gs_read(gs, b, blen) +gifState *gs; +byte **b; +int blen; +{ + if (gs->datalen < blen + gs->pos) return(0); + + *b = gs->data + gs->pos; + gs->pos += blen; + + return(blen); +} + +/* + * gs_read_sig + * + */ +static int +gs_read_sig(gs) +gifState *gs; +{ + byte *b; + + /* check GIF signature */ + if (gs_read(gs, &b, GIF_SIG_LEN) != GIF_SIG_LEN) return(GS_NEED_DATA); + + if ((strncmp((char *)b, GIF_SIG, strlen(GIF_SIG)) != 0) && + (strncmp((char *)b, GIF_SIG_89, strlen(GIF_SIG_89)) != 0)) + { + return(GS_ERR_BAD_SIG); + } + + gs->state = GS_OPEN_FILE; + + return(GS_SUCCESS); +} + +/* + * gs_open_file + * + * open a GIF file + */ +static int +gs_open_file(gs) +gifState *gs; +{ + byte *b; + + /* read screen descriptor */ + if (gs_read(gs, &b, GIF_SD_SIZE) != GIF_SD_SIZE) return(GS_NEED_DATA); + + /* decode screen descriptor */ + gs->rast_width = (b[1] << 8) + b[0]; + gs->rast_height = (b[3] << 8) + b[2]; + gs->g_cmap_flag = (b[4] & 0x80) ? 1 : 0; + gs->color_bits = ((b[4] & 0x70) >> 4) + 1; + gs->g_pixel_bits = (b[4] & 0x07) + 1; + gs->bg_color = b[5]; + + /* load global colormap */ + if (gs->g_cmap_flag) + { + gs->g_ncolors = (1 << gs->g_pixel_bits); + gs->g_ncolors_pos = 0; + gs->state = GS_LOAD_G_CMAP; + } + else + { + gs->state = GS_OPEN_IMAGE; + gs->g_ncolors_pos = 0; + gs->g_ncolors = 0; + } + + gs->have_dimensions = 1; + + return(GS_SUCCESS); +} + +/* + * gs_init_decoder + * + * This isn't a separate state. + */ +static int +gs_init_decoder(gs) +gifState *gs; +{ + int i; + byte *b; + + if (gs_read(gs, &b, 1) != 1) return(GS_NEED_DATA); + + gs->root_size = (int)(*b); + gs->clr_code = 1 << gs->root_size; + gs->eoi_code = gs->clr_code + 1; + gs->table_size = gs->clr_code + 2; + gs->code_size = gs->root_size + 1; + gs->code_mask = (1 << gs->code_size) - 1; + gs->work_bits = 0; + gs->work_data = 0; + gs->buf_idx = 0; + gs->buf_cnt = 0; + gs->first = 0; + + /* initialize string table */ + for (i = 0; i < STAB_SIZE; i++) + { + gs->prefix[i] = NULL_CODE; + gs->extnsn[i] = i; + } + + /* initialize pixel stack */ + gs->pstk_idx = 0; + + gs->state = GS_MAKE_IMAGE; + + return(GS_SUCCESS); +} + +/* + * gs_open_image + */ +static int +gs_open_image(gs) +gifState *gs; +{ + int separator; + byte *b; + int rval; + int pos = gs->pos; + + if (gs_read(gs, &b, 1) != 1) return(GS_NEED_DATA); + + separator = (int)(*b); + if (separator == GIF_EXTENSION) + { + /* get the extension function byte */ + if (gs_read(gs, &b, 1) != 1) + { + gs->pos = pos; + return(GS_NEED_DATA); + } + + if (*b == 0xf9) + { + if ((rval = gs_read_data_block(gs)) != GS_SUCCESS) + { + gs->pos = pos; + return(rval); + } + if (gs->buf[0] & 0x1) gs->transparent = gs->buf[3]; + } + else + { + if ((rval = gs_read_data_block(gs)) != GS_SUCCESS) + { + gs->pos = pos; + return(rval); + } + } + + return(GS_SUCCESS); + } + else if (separator == GIF_TERMINATOR) return(GS_DONE); + /* + * If it's a broken GIF, just keep scanning till we meet a + * separator we recognise. + */ + else if (separator != GIF_SEPARATOR) return(GS_SUCCESS); + + gs->state = GS_READ_IMAGE_HEADER; + + return(GS_SUCCESS); +} + +/* + * gs_read_image_header + */ +static int +gs_read_image_header(gs) +gifState *gs; +{ + byte *b; + + /* read image descriptor */ + if (gs_read(gs, &b, GIF_ID_SIZE) != GIF_ID_SIZE) return(GS_NEED_DATA); + + /* decode image descriptor */ + gs->img_left = (b[1] << 8) + b[0]; + gs->img_top = (b[3] << 8) + b[2]; + gs->img_width = (b[5] << 8) + b[4]; + gs->img_height = (b[7] << 8) + b[6]; + gs->l_cmap_flag = (b[8] & 0x80) ? 1 : 0; + gs->interlace_flag = (b[8] & 0x40) ? 1 : 0; + gs->l_pixel_bits = (b[8] & 0x07) + 1; + + /* load local colormap */ + if (gs->l_cmap_flag) + { + gs->l_ncolors = (1 << gs->l_pixel_bits); + gs->l_ncolors_pos = 0; + gs->state = GS_LOAD_L_CMAP; + } + else + { + gs->l_ncolors = 0; + gs->l_ncolors_pos = 0; + gs->state = GS_INIT_DECODER; + } + + return(GS_SUCCESS); +} + +/* + * gs_load_l_cmap + * + * load a local colormap from the input stream + */ +static int +gs_load_l_cmap(gs) +gifState *gs; +{ + byte *b; + + if (gs_read(gs, &b, 3) != 3) return(GS_NEED_DATA); + + gs->l_cmap[GIF_RED][gs->l_ncolors_pos] = b[GIF_RED] << 8; + gs->l_cmap[GIF_GRN][gs->l_ncolors_pos] = b[GIF_GRN] << 8; + gs->l_cmap[GIF_BLU][gs->l_ncolors_pos] = b[GIF_BLU] << 8; + + gs->l_ncolors_pos++; + if (gs->l_ncolors_pos == gs->l_ncolors) gs->state = GS_INIT_DECODER; + + return(GS_SUCCESS); +} + +/* + * gs_load_g_cmap + * + * load a global colormap from the input stream + */ +static int +gs_load_g_cmap(gs) +gifState *gs; +{ + byte *b; + + if (gs_read(gs, &b, 3) != 3) return(GS_NEED_DATA); + + gs->g_cmap[GIF_RED][gs->g_ncolors_pos] = b[GIF_RED] << 8; + gs->g_cmap[GIF_GRN][gs->g_ncolors_pos] = b[GIF_GRN] << 8; + gs->g_cmap[GIF_BLU][gs->g_ncolors_pos] = b[GIF_BLU] << 8; + + gs->g_ncolors_pos++; + if (gs->g_ncolors_pos == gs->g_ncolors) gs->state = GS_OPEN_IMAGE; + + return(GS_SUCCESS); +} + +/* + * gs_read_data_block + * + * read a new data block from the input stream + */ +static int +gs_read_data_block(gs) +gifState *gs; +{ + byte *b; + int cnt; + int pos = gs->pos; + + /* read the data block header */ + if (gs_read(gs, &b, 1) != 1) return(GS_NEED_DATA); + cnt = (int)(*b); + + /* read the data block body */ + if (gs_read(gs, &gs->buf, cnt) != cnt) + { + gs->pos = pos; + return(GS_NEED_DATA); + } + + gs->buf_idx = 0; + gs->buf_cnt = cnt; + + return(GS_SUCCESS); +} + +/* + * gs_make_image + */ +static int +gs_make_image(gs) +gifState *gs; +{ + Image *image; + int i; + bool gray = true; + + if (gs->l_cmap_flag) + { + image = newRGBImage(gs->img_width, gs->img_height, gs->l_pixel_bits); + if (!image) return(GS_ERR_NOMEM); + + image->rgb.red = gs->l_cmap[GIF_RED]; + image->rgb.green = gs->l_cmap[GIF_GRN]; + image->rgb.blue = gs->l_cmap[GIF_BLU]; + image->rgb.used = gs->l_ncolors; + } + else + { + image = newRGBImage(gs->img_width, gs->img_height, gs->g_pixel_bits); + if (!image) return(GS_ERR_NOMEM); + + image->rgb.red = gs->g_cmap[GIF_RED]; + image->rgb.green = gs->g_cmap[GIF_GRN]; + image->rgb.blue = gs->g_cmap[GIF_BLU]; + image->rgb.used = gs->g_ncolors; + } + + for(i = 0; i < image->rgb.used; i++) + if(image->rgb.red[i] >> 8 != image->rgb.green[i] >> 8 || + image->rgb.red[i] >> 8 != image->rgb.blue[i] >> 8) + { + gray = false; + break; + } + if(gray) image->type = IGRAY; + + image->transparent = gs->transparent; + gs->image = image; + gs->state = GS_DECODE_DATA; + image->x = 0; + image->pass = 0; + if (gs->interlace_flag) image->y = interlace_start[image->pass]; + else image->y = 0; + + return(GS_SUCCESS); +} + +/* + * gs_get_pixel + * + * try to read next pixel from the raster, return result in *pel + */ +static int +gs_get_pixel(gs, pel) +gifState *gs; +int *pel; +{ + int code; + int rval; + + /* decode until there are some pixels on the pixel stack */ + while (gs->pstk_idx == 0) + { + while (gs->work_bits < gs->code_size) + { + int pos = gs->pos; + + if (gs->buf_idx == gs->buf_cnt) + { + if ((rval = gs_read_data_block(gs)) != GS_SUCCESS) + { + gs->pos = pos; + return(rval); + } + } + + gs->work_data |= ((unsigned) gs->buf[gs->buf_idx++]) << gs->work_bits; + gs->work_bits += 8; + } + + /* get the next code */ + code = gs->work_data & gs->code_mask; + gs->work_data >>= gs->code_size; + gs->work_bits -= gs->code_size; + + /* interpret the code */ + if (code > gs->table_size || code == gs->eoi_code) + { + /* Format Error */ + return(GS_ERR_EOF); + } + + if (code == gs->clr_code) + { + /* reset decoder */ + gs->code_size = gs->root_size + 1; + gs->code_mask = (1 << gs->code_size) - 1; + gs->table_size = gs->clr_code + 2; + gs->prev_code = NULL_CODE; + continue; + } + + if (gs->prev_code == NULL_CODE) + { + gs->pstk[gs->pstk_idx++] = gs->extnsn[code]; + gs->prev_code = code; + gs->first = code; + continue; + } + else + { + int in_code = code; + + if (code == gs->table_size) + { + gs->pstk[gs->pstk_idx++] = gs->first; + code = gs->prev_code; + } + while (code > gs->clr_code) + { + gs->pstk[gs->pstk_idx++] = gs->extnsn[code]; + code = gs->prefix[code]; + } + gs->first = gs->extnsn[code]; + /* Add a new string to the string table. */ + if (gs->table_size >= PSTK_SIZE) + return(GS_ERR_EOF); + gs->pstk[gs->pstk_idx++] = gs->first; + gs->prefix[gs->table_size] = gs->prev_code; + gs->extnsn[gs->table_size] = gs->first; + gs->table_size ++; + if (((gs->table_size & gs->code_mask)) == 0 + && (gs->table_size < PSTK_SIZE)) + { + gs->code_size ++; + gs->code_mask += gs->table_size; + } + gs->prev_code = in_code; + } + } + + *pel = (int)gs->pstk[--gs->pstk_idx]; + + return(GS_SUCCESS); +} + +/* + * gs_decode_data + * + * TODO: This routine looks like something of a performance catastrophe + */ +static int +gs_decode_data(gs) +gifState *gs; +{ + Image *image = gs->image; + int pixel; + byte *pixptr; + int rval; + int xpix; + + if (gs->interlace_flag) + { + if (image->x == image->width) + { + image->y += interlace_rate[image->pass]; + while (image->y >= image->height) + { + image->pass++; + if (image->pass == 4) return(GS_DONE); + image->y = interlace_start[image->pass]; + } + image->x = 0; + } + } + else + { + if (image->x == image->width) + { + image->y++; + image->x = 0; + } + if (image->y == image->height) return(GS_DONE); + } + + xpix = image->y * image->bytes_per_line + + (image->x * image->pixlen) / CHAR_BITS; + + if (xpix < image->height * image->bytes_per_line) + { + pixptr = image->data + xpix; + if ((rval = gs_get_pixel(gs, &pixel)) != GS_SUCCESS) return(rval); + if(image->pixlen == 1) + { + int bitpos = image->x & 7; + if (bitpos == 0) *pixptr = 0; +# ifdef CHIMERA_LITTLE_ENDIAN + pixel <<= bitpos; +# else + pixel <<= 7 - bitpos; +# endif + *pixptr |= pixel; + } + else /* pixlen is 8 bits */ + { + *pixptr = pixel; + } + } + + image->x++; + + if (image->x == image->width) + { + if (gs->interlace_flag && interlace_copy[image->pass] > 1) + { + byte *origline = image->data + image->y * image->bytes_per_line; + byte *copyline = origline + image->bytes_per_line; + int i; + for (i = 1; + i < interlace_copy[image->pass] && image->y + i < image->height; + i++, copyline += image->bytes_per_line) + { + memcpy(copyline, origline, image->bytes_per_line); + } + if (gs->lineProc != NULL) + (gs->lineProc)(gs->closure, + image->y, + MIN(image->y + interlace_copy[image->pass] - 1, + image->height - 1)); + } + else + { + if (gs->lineProc != NULL) + (gs->lineProc)(gs->closure, image->y, image->y); + } + } + + return(GS_SUCCESS); +} + +/* + * gifDestroy + * + * This doesn't do the whole job! + */ +static void +gifDestroy(pointer) +void *pointer; +{ + gifState *gs = (gifState *)pointer; + if (gs->image) + freeImage(gs->image); + gs->image = 0; + + if (gs != NULL) free_mem(gs); + + return; +} + +/* + * gifAddData + * + * 0 success + * 1 needs more data + * -1 error + * + * Assumes data is the address of the beginning of the GIF data and len + * is the total length. + */ +static int +gifAddData(pointer, data, len, data_ended) +void *pointer; +byte *data; +int len; +bool data_ended; +{ + gifState *gs = (gifState *)pointer; + int rval; + int pos; + + gs->data = data; + gs->datalen = len; + + for ( ; ; ) + { + pos = gs->pos; /* save in case we need to rewind */ + switch (gs->state) + { + case GS_READ_SIG: + rval = gs_read_sig(gs); + break; + case GS_OPEN_FILE: + rval = gs_open_file(gs); + break; + case GS_OPEN_IMAGE: + if ((rval = gs_open_image(gs)) == GS_DONE) return(0); + break; + case GS_READ_IMAGE_HEADER: + rval = gs_read_image_header(gs); + break; + case GS_LOAD_G_CMAP: + rval = gs_load_g_cmap(gs); + break; + case GS_LOAD_L_CMAP: + rval = gs_load_l_cmap(gs); + break; + case GS_MAKE_IMAGE: + rval = gs_make_image(gs); + break; + case GS_DECODE_DATA: + if ((rval = gs_decode_data(gs)) == GS_DONE) return(0); + break; + case GS_INIT_DECODER: + rval = gs_init_decoder(gs); + break; + default: + rval = GS_ERR_BAD_STATE; + } + if (rval == GS_NEED_DATA) + { + gs->pos = pos; + if(data_ended) return -1; + return(1); + } + else if (rval != GS_SUCCESS) return(-1); + } + + return(-1); +} + +static Image * +gifGetImage(void *pointer) +{ + gifState *gs = (gifState *)pointer; + return gs->image; +} + +/* + * gifInit + * + * Initialize GIF reader state + */ +void +gifInit(lineProc, closure, if_vector) +FormatLineProc lineProc; +void *closure; +struct ifs_vector *if_vector; +{ + gifState *gs; + + gs = (gifState *)alloc_mem(sizeof(gifState)); + memset(gs, 0, sizeof(gifState)); + gs->state = GS_READ_SIG; + gs->transparent = -1; + gs->lineProc = lineProc; + gs->closure = closure; + + if_vector->image_format_closure = (void *)gs; + if_vector->initProc = &gifInit; + if_vector->destroyProc = &gifDestroy; + if_vector->addDataProc = &gifAddData; + if_vector->getImageProc = &gifGetImage; +} + + + + diff --git a/image/gif.h b/image/gif.h new file mode 100644 index 0000000..e4d40a0 --- /dev/null +++ b/image/gif.h @@ -0,0 +1,91 @@ +/* + * gif.h + * + * Copyright (C) 1995, John Kilburg (john@isri.unlv.edu) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef __GIF_H__ +#define __GIF_H__ 1 + +#define STAB_SIZE 4096 /* string table size */ +#define PSTK_SIZE 4096 /* pixel stack size */ + + +/* + * gifState + */ +typedef struct _gif_state +{ + int state; /* state of GIF reader */ + void (*lineProc)(); /* line callback */ + void *closure; /* closure for callback */ + + Image *image; + + int root_size; /* root code size */ + int clr_code; /* clear code */ + int eoi_code; /* end of information code */ + int code_size; /* current code size */ + int code_mask; /* current code mask */ + int prev_code; /* previous code */ + + int work_data; /* working bit buffer */ + int work_bits; /* working bit count */ + + byte *buf; /* byte buffer */ + int buf_cnt; /* byte count */ + int buf_idx; /* buffer index */ + + int table_size; /* string table size */ + int prefix[STAB_SIZE]; /* string table : prefixes */ + int extnsn[STAB_SIZE]; /* string table : extensions */ + + byte pstk[PSTK_SIZE]; /* pixel stack */ + int pstk_idx; /* pixel stack pointer */ + + int pos; /* current read position */ + byte *data; /* pointer to data */ + int datalen; /* length of data */ + int rast_width; /* raster width */ + int rast_height; /* raster height */ + byte g_cmap_flag; /* global colormap flag */ + int g_pixel_bits; /* bits per pixel, global colormap */ + int g_ncolors; /* number of colors, global colormap */ + int g_ncolors_pos; /* number of colors processed, global colormap */ + Intensity g_cmap[3][256]; /* global colormap */ + int bg_color; /* background color index */ + int color_bits; /* bits of color resolution */ + int transparent; /* transparent color index */ + + int img_left; /* image position on raster */ + int img_top; /* image position on raster */ + int img_width; /* image width */ + int img_height; /* image height */ + byte have_dimensions; /* 1 when dimensions known */ + byte l_cmap_flag; /* local colormap flag */ + int l_pixel_bits; /* bits per pixel, local colormap */ + int l_ncolors; /* number of colors, local colormap */ + int l_ncolors_pos; /* number of colors processed, local colormap */ + Intensity l_cmap[3][256]; /* local colormap */ + byte interlace_flag; /* interlace image format flag */ +} gifState; + +gifState *gifInit _ArgProto((void (*)(), void *)); +void gifDestroy _ArgProto((gifState *)); +int gifAddData _ArgProto((gifState *, byte *, int)); + +#endif diff --git a/image/gifp.h b/image/gifp.h new file mode 100644 index 0000000..0c614c8 --- /dev/null +++ b/image/gifp.h @@ -0,0 +1,145 @@ +/* + * gifp.h + * + * Created from parts of old gifin.h + * John Kilburg (john@cs.unlv.edu) + * + * gifin.h + * kirk johnson + * november 1989 + * external interface to gifin.c + * + * Copyright 1989 Kirk L. Johnson + * See the file COPYRIGHT for details + */ + +/* + * the old gif.h file + * + * Copyright (C) 1995, John Kilburg (john@isri.unlv.edu) + */ + +#define STAB_SIZE 4096 /* string table size */ +#define PSTK_SIZE 4096 /* pixel stack size */ + + +/* + * gifState + */ +typedef struct _gif_state +{ + int state; /* state of GIF reader */ + FormatLineProc lineProc; /* line callback */ + void *closure; /* closure for callback */ + + Image *image; + + int root_size; /* root code size */ + int clr_code; /* clear code */ + int eoi_code; /* end of information code */ + int code_size; /* current code size */ + int code_mask; /* current code mask */ + int prev_code; /* previous code */ + int first; /* */ + + unsigned work_data; /* working bit buffer */ + int work_bits; /* working bit count */ + + byte *buf; /* byte buffer */ + int buf_cnt; /* byte count */ + int buf_idx; /* buffer index */ + + int table_size; /* string table size */ + int prefix[STAB_SIZE]; /* string table : prefixes */ + int extnsn[STAB_SIZE]; /* string table : extensions */ + + byte pstk[PSTK_SIZE]; /* pixel stack */ + int pstk_idx; /* pixel stack pointer */ + + int pos; /* current read position */ + byte *data; /* pointer to data */ + int datalen; /* length of data */ + int rast_width; /* raster width */ + int rast_height; /* raster height */ + byte g_cmap_flag; /* global colormap flag */ + int g_pixel_bits; /* bits per pixel, global colormap */ + int g_ncolors; /* number of colors, global colormap */ + int g_ncolors_pos; /* number of colors processed, global colormap */ + Intensity g_cmap[3][256]; /* global colormap */ + int bg_color; /* background color index */ + int color_bits; /* bits of color resolution */ + int transparent; /* transparent color index */ + + int img_left; /* image position on raster */ + int img_top; /* image position on raster */ + int img_width; /* image width */ + int img_height; /* image height */ + byte have_dimensions; /* 1 when dimensions known */ + byte l_cmap_flag; /* local colormap flag */ + int l_pixel_bits; /* bits per pixel, local colormap */ + int l_ncolors; /* number of colors, local colormap */ + int l_ncolors_pos; /* number of colors processed, local colormap */ + Intensity l_cmap[3][256]; /* local colormap */ + byte interlace_flag; /* interlace image format flag */ +} gifState; + +/* + * end of old gif.h file + */ + +/* + * gifin return codes + */ +#define GS_SUCCESS 0 /* success */ +#define GS_DONE 1 /* no more images */ +#define GS_NEED_DATA 2 /* needs more data */ +#define GS_NEED_DATA_NOR 3 /* needs more data, no rewind */ + +#define GS_ERR_BAD_SD -1 /* bad screen descriptor */ +#define GS_ERR_BAD_SEP -2 /* bad image separator */ +#define GS_ERR_BAD_SIG -3 /* bad signature */ +#define GS_ERR_EOD -4 /* unexpected end of raster data */ +#define GS_ERR_EOF -5 /* unexpected end of input stream */ +#define GS_ERR_FAO -6 /* file already open */ +#define GS_ERR_IAO -7 /* image already open */ +#define GS_ERR_NFO -8 /* no file open */ +#define GS_ERR_NIO -9 /* no image open */ +#define GS_ERR_BAD_STATE -10 /* bad state */ +#define GS_ERR_NOMEM -11 /* allocation failure */ + +/* + * colormap indices + */ +#define GIF_RED 0 +#define GIF_GRN 1 +#define GIF_BLU 2 + +/* + * #defines, typedefs, and such + */ +#define GIF_SIG "GIF87a" +#define GIF_SIG_89 "GIF89a" +#define GIF_SIG_LEN 6 /* GIF signature length */ +#define GIF_SD_SIZE 7 /* GIF screen descriptor size */ +#define GIF_ID_SIZE 9 /* GIF image descriptor size */ + +#define GIF_SEPARATOR ',' /* GIF image separator */ +#define GIF_EXTENSION '!' /* GIF extension block marker */ +#define GIF_TERMINATOR ';' /* GIF terminator */ + +#define NULL_CODE -1 /* string table null code */ + +/* + * GIF states + */ +#define GS_OPEN_FILE 0 +#define GS_OPEN_IMAGE 1 +#define GS_LOAD_G_CMAP 2 +#define GS_LOAD_L_CMAP 4 +#define GS_DECODE_DATA 5 +#define GS_MAKE_IMAGE 6 +#define GS_INIT_DECODER 8 +#define GS_READ_SIG 9 +#define GS_READ_IMAGE_HEADER 10 + + diff --git a/image/halftone.c b/image/halftone.c new file mode 100644 index 0000000..3a80086 --- /dev/null +++ b/image/halftone.c @@ -0,0 +1,127 @@ +/* + * halftone.c + * + * Copyright (c) 1995 Erik Corry + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* RGB intensity tables. + */ + +unsigned short RedIntensity[256]= { + 0, 77, 154, 231, 308, 385, 462, 539, + 616, 693, 770, 847, 924, 1001, 1078, 1155, + 1232, 1309, 1386, 1463, 1540, 1617, 1694, 1771, + 1848, 1925, 2002, 2079, 2156, 2233, 2310, 2387, + 2464, 2541, 2618, 2695, 2772, 2849, 2926, 3003, + 3080, 3157, 3234, 3311, 3388, 3465, 3542, 3619, + 3696, 3773, 3850, 3927, 4004, 4081, 4158, 4235, + 4312, 4389, 4466, 4543, 4620, 4697, 4774, 4851, + 4928, 5005, 5082, 5159, 5236, 5313, 5390, 5467, + 5544, 5621, 5698, 5775, 5852, 5929, 6006, 6083, + 6160, 6237, 6314, 6391, 6468, 6545, 6622, 6699, + 6776, 6853, 6930, 7007, 7084, 7161, 7238, 7315, + 7392, 7469, 7546, 7623, 7700, 7777, 7854, 7931, + 8008, 8085, 8162, 8239, 8316, 8393, 8470, 8547, + 8624, 8701, 8778, 8855, 8932, 9009, 9086, 9163, + 9240, 9317, 9394, 9471, 9548, 9625, 9702, 9779, + 9856, 9933,10010,10087,10164,10241,10318,10395, +10472,10549,10626,10703,10780,10857,10934,11011, +11088,11165,11242,11319,11396,11473,11550,11627, +11704,11781,11858,11935,12012,12089,12166,12243, +12320,12397,12474,12551,12628,12705,12782,12859, +12936,13013,13090,13167,13244,13321,13398,13475, +13552,13629,13706,13783,13860,13937,14014,14091, +14168,14245,14322,14399,14476,14553,14630,14707, +14784,14861,14938,15015,15092,15169,15246,15323, +15400,15477,15554,15631,15708,15785,15862,15939, +16016,16093,16170,16247,16324,16401,16478,16555, +16632,16709,16786,16863,16940,17017,17094,17171, +17248,17325,17402,17479,17556,17633,17710,17787, +17864,17941,18018,18095,18172,18249,18326,18403, +18480,18557,18634,18711,18788,18865,18942,19019, +19096,19173,19250,19327,19404,19481,19558,19635 +}; + +unsigned short GreenIntensity[256]= { + 0, 150, 300, 450, 600, 750, 900, 1050, + 1200, 1350, 1500, 1650, 1800, 1950, 2100, 2250, + 2400, 2550, 2700, 2850, 3000, 3150, 3300, 3450, + 3600, 3750, 3900, 4050, 4200, 4350, 4500, 4650, + 4800, 4950, 5100, 5250, 5400, 5550, 5700, 5850, + 6000, 6150, 6300, 6450, 6600, 6750, 6900, 7050, + 7200, 7350, 7500, 7650, 7800, 7950, 8100, 8250, + 8400, 8550, 8700, 8850, 9000, 9150, 9300, 9450, + 9600, 9750, 9900,10050,10200,10350,10500,10650, +10800,10950,11100,11250,11400,11550,11700,11850, +12000,12150,12300,12450,12600,12750,12900,13050, +13200,13350,13500,13650,13800,13950,14100,14250, +14400,14550,14700,14850,15000,15150,15300,15450, +15600,15750,15900,16050,16200,16350,16500,16650, +16800,16950,17100,17250,17400,17550,17700,17850, +18000,18150,18300,18450,18600,18750,18900,19050, +19200,19350,19500,19650,19800,19950,20100,20250, +20400,20550,20700,20850,21000,21150,21300,21450, +21600,21750,21900,22050,22200,22350,22500,22650, +22800,22950,23100,23250,23400,23550,23700,23850, +24000,24150,24300,24450,24600,24750,24900,25050, +25200,25350,25500,25650,25800,25950,26100,26250, +26400,26550,26700,26850,27000,27150,27300,27450, +27600,27750,27900,28050,28200,28350,28500,28650, +28800,28950,29100,29250,29400,29550,29700,29850, +30000,30150,30300,30450,30600,30750,30900,31050, +31200,31350,31500,31650,31800,31950,32100,32250, +32400,32550,32700,32850,33000,33150,33300,33450, +33600,33750,33900,34050,34200,34350,34500,34650, +34800,34950,35100,35250,35400,35550,35700,35850, +36000,36150,36300,36450,36600,36750,36900,37050, +37200,37350,37500,37650,37800,37950,38100,38250 +}; + +unsigned short BlueIntensity[256]= { + 0, 29, 58, 87, 116, 145, 174, 203, + 232, 261, 290, 319, 348, 377, 406, 435, + 464, 493, 522, 551, 580, 609, 638, 667, + 696, 725, 754, 783, 812, 841, 870, 899, + 928, 957, 986, 1015, 1044, 1073, 1102, 1131, + 1160, 1189, 1218, 1247, 1276, 1305, 1334, 1363, + 1392, 1421, 1450, 1479, 1508, 1537, 1566, 1595, + 1624, 1653, 1682, 1711, 1740, 1769, 1798, 1827, + 1856, 1885, 1914, 1943, 1972, 2001, 2030, 2059, + 2088, 2117, 2146, 2175, 2204, 2233, 2262, 2291, + 2320, 2349, 2378, 2407, 2436, 2465, 2494, 2523, + 2552, 2581, 2610, 2639, 2668, 2697, 2726, 2755, + 2784, 2813, 2842, 2871, 2900, 2929, 2958, 2987, + 3016, 3045, 3074, 3103, 3132, 3161, 3190, 3219, + 3248, 3277, 3306, 3335, 3364, 3393, 3422, 3451, + 3480, 3509, 3538, 3567, 3596, 3625, 3654, 3683, + 3712, 3741, 3770, 3799, 3828, 3857, 3886, 3915, + 3944, 3973, 4002, 4031, 4060, 4089, 4118, 4147, + 4176, 4205, 4234, 4263, 4292, 4321, 4350, 4379, + 4408, 4437, 4466, 4495, 4524, 4553, 4582, 4611, + 4640, 4669, 4698, 4727, 4756, 4785, 4814, 4843, + 4872, 4901, 4930, 4959, 4988, 5017, 5046, 5075, + 5104, 5133, 5162, 5191, 5220, 5249, 5278, 5307, + 5336, 5365, 5394, 5423, 5452, 5481, 5510, 5539, + 5568, 5597, 5626, 5655, 5684, 5713, 5742, 5771, + 5800, 5829, 5858, 5887, 5916, 5945, 5974, 6003, + 6032, 6061, 6090, 6119, 6148, 6177, 6206, 6235, + 6264, 6293, 6322, 6351, 6380, 6409, 6438, 6467, + 6496, 6525, 6554, 6583, 6612, 6641, 6670, 6699, + 6728, 6757, 6786, 6815, 6844, 6873, 6902, 6931, + 6960, 6989, 7018, 7047, 7076, 7105, 7134, 7163, + 7192, 7221, 7250, 7279, 7308, 7337, 7366, 7395 +}; diff --git a/image/image.c b/image/image.c new file mode 100644 index 0000000..7462198 --- /dev/null +++ b/image/image.c @@ -0,0 +1,1254 @@ +/* + * image.c + * + * Copyright (c) 1995 Erik Corry ehcorry@inet.uni-c.dk + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + */ + +#include "port_before.h" + +#include + +#ifdef HAVE_STDLIB_H +#include +#endif + +#include +#include + +#include "port_after.h" + +#include "colorcube.h" +#include "dispdither.h" +#include "xcolorcube.h" +#include "image_endian.h" +#include "imagep.h" +#include "image_format.h" + +#include "Chimera.h" +#include "ChimeraGUI.h" +#include "ChimeraRender.h" + +#define TCOUNT 4 + +typedef void (*lt_expansion_fn) _ArgProto((byte *, byte *, + int, Intensity *, Intensity *, + Intensity *, int, + Intensity, Intensity, + Intensity)); + +#define IMAGE_GIF 0 +#define IMAGE_XBM 1 +#define IMAGE_JPEG 2 +#define IMAGE_PNG 3 +#define IMAGE_PNM 5 +#define IMAGE_UNKNOWN 6 + +static struct content_map +{ + char *name; + int id; +} content_map[] = +{ + { "image/gif", IMAGE_GIF }, + { "image/xbm", IMAGE_XBM }, + { "image/pnm", IMAGE_PNM }, +#ifdef HAVE_JPEG + { "image/jpeg", IMAGE_JPEG }, +#endif +#ifdef HAVE_PNG + { "image/x-png", IMAGE_PNG }, + { "image/png", IMAGE_PNG }, +#endif + { "image/x-xbitmap", IMAGE_XBM }, + { "image/x-portable-anymap", IMAGE_PNM }, + { "image/x-portable-bitmap", IMAGE_PNM }, + { "image/x-portable-graymap", IMAGE_PNM }, + { "image/x-portable-pixmap", IMAGE_PNM }, + { NULL, IMAGE_UNKNOWN }, +}; + +/* + * Holds the infomation about a particular display + */ +typedef struct imagestate +{ + MemPool mp; + + /* X information */ + Display *dpy; + Colormap cmap; + int depth; + Visual *v; + Widget w; + Window win; /* draw window */ + Pixel bg, fg; /* background, foreground for bitmaps */ + XColor bgcolor; + + XImage *xi; + int last_line; + int ref_count; + char *hash; + + /* Generic Image decoding vars */ + cct_dither_table dither_table[TCOUNT];/* Table to dither/convert to screen */ + int free_dither_table; /* How many tables were allocated (0-4) */ + ddt_dither_fn dither_function; /* Function to dither/convert with */ + bool dithering; + + struct ifs_vector if_vector; /* Image format decoder info */ + + byte *expansion_buf; /* Area for upgrading to bigger depth */ + lt_expansion_fn expansion_function; /* Func for upgrading to bigger depth */ + + int expanded_type; /* Image type after expansion */ + int image_special_count; /* colors allocated for this image */ + int image_special_max; /* max colors to alloc for this image */ + bool no_more_specials; + bool special_blacklist[256]; /* already tried to allocate pixel once */ + bool special_real_blacklist[256]; /* really tried to allocate pixel once */ + + /* control stuff */ + ChimeraSink wp; + ChimeraGUI wd; + struct imageclass *ic; + struct imagestate *prev, *next; +} ImageState; + +/* + * Hold global information about the image module + */ +typedef struct imageclass +{ + MemPool mp; + int icount; + bool init; + GC gc; + int refcount; + + /* Image decoding stuff */ + cct_cube colorcube; /* cuboid of allocated colors on screen */ + cct_cube grayscale; /* gray shades allocated on screen */ + cct_dither_table color_tables[TCOUNT]; /* color dither table */ + cct_dither_table gray_tables[TCOUNT]; /* color dither table */ + struct ccs_special_color special_colors[256]; /* specially alloc'd colors */ + int special_count; + int special_max; +} ImageClass; + +void SetSize _ArgProto((ImageState *)); +static void ImageToXImage _ArgProto((void *, int, int)); +static Image *lf_get_image _ArgProto((ImageState *)); +static void lf_set_up_bitmap _ArgProto((ImageState *)); +static void lf_set_up_expansion _ArgProto((ImageState *)); +static byte *lf_get_8_bit_map _ArgProto((MemPool, Intensity *)); +static bool lf_null_gray_map _ArgProto((byte **)); +static void lf_set_up_dither _ArgProto((ImageState *)); +static void lf_get_new_specials _ArgProto((ImageState *, ImageClass *, + Image *, byte *)); +static void ImageClassInit _ArgProto((ImageClass *, Display *)); +static void ImageAdd _ArgProto((void *)); +static void ImageEnd _ArgProto((void *)); +static void ImageDestroy _ArgProto((void *)); +void *ImageInit _ArgProto((ChimeraRender, void *, void *)); +static bool ImageExpose _ArgProto((void *, int, int, + unsigned int, unsigned int)); + +static byte * +lf_get_8_bit_map(mp, old_table) +MemPool mp; +Intensity *old_table; +{ + byte *answer = (byte *)MPCGet(mp, sizeof(byte) * 256); + int i; + + for (i = 0; i < 256; i++) + answer[i] = old_table[i] >> 8; + + return answer; +} + +static bool +lf_null_gray_map(maps) +byte *maps[3]; +{ + int i; + for (i = 0; i < 256; i++) + { + if (maps[0][i] != maps[1][i]) return false; + if (maps[2][i] != maps[1][i]) return false; + if (maps[2][i] != i) return false; + } + return true; +} + +static Image * +lf_get_image(is) +ImageState *is; +{ + if (is->if_vector.getImageProc && is->if_vector.image_format_closure) + return(is->if_vector.getImageProc(is->if_vector.image_format_closure)); + return(NULL); +} + +static void +lf_set_up_bitmap(is) +ImageState *is; +{ + Image *image = lf_get_image(is); + ImageClass *ic = is->ic; + bool really_allocated = false; + int i; + Intensity intensities[3]; + + is->expanded_type = image->type; + + if(image->type == IBITMAP) + { + is->bg = is->bgcolor.pixel; + is->fg = BlackPixel(is->dpy, DefaultScreen(is->dpy)); + return; + } + + for(i = 0; i < 2; i++) + { + bool really_allocated_last_time = really_allocated; + if(image->transparent != i) + { + intensities[0] = image->rgb.red[i]; + intensities[1] = image->rgb.green[i]; + intensities[2] = image->rgb.blue[i]; + if (!xccf_allocate_special(is->dpy, + is->cmap, + ic->colorcube, + ic->grayscale, + intensities, + &really_allocated, + /* don't-really-allocate-flag */ + ic->special_count >= ic->special_max, + ic->special_colors, + ic->special_count, + i ? &(is->fg): &(is->bg))) + { + if(really_allocated_last_time) + { + XFreeColors(is->dpy, + is->cmap, + &(ic->special_colors[ic->special_count - 1].pixel), + 1, + 0); + ic->special_count--; + } + + if(image->rgb.red[0] == image->rgb.green[0] && + image->rgb.red[0] == image->rgb.blue[0] && + image->rgb.red[1] == image->rgb.green[1] && + image->rgb.red[1] == image->rgb.blue[1]) + { + is->expanded_type = IGRAY; + is->expansion_buf = MPCGet(is->mp, image->width + 64); + } + else + { + is->expanded_type = ITRUE; + is->expansion_buf = MPCGet(is->mp, (3 * image->width) + 192); + } + + return; + } + if(really_allocated) ic->special_count++; + } + else + { + if (i) is->fg = is->bgcolor.pixel; + else is->bg = is->bgcolor.pixel; + } + } + + return; +} + +static void +lf_expand_bitmap_to_true( + byte *expansion_buffer, + byte *source_buffer, + int width, + Intensity *red_cmap, + Intensity *green_cmap, + Intensity *blue_cmap, + int transparent, + Intensity red_bg, + Intensity green_bg, + Intensity blue_bg) +{ + int i; + byte red[2]; + byte green[2]; + byte blue[2]; + red[0] = red_cmap[0] >> 8; + red[1] = red_cmap[1] >> 8; + green[0] = green_cmap[0] >> 8; + green[1] = green_cmap[1] >> 8; + blue[0] = blue_cmap[0] >> 8; + blue[1] = blue_cmap[1] >> 8; + if(transparent != -1) + { + red[transparent] = red_bg >> 8; + green[transparent] = green_bg >> 8; + blue[transparent] = blue_bg >> 8; + } + for(i = (width + CHAR_BITS - 1) / CHAR_BITS; i; i--) + { + byte t = *source_buffer++; +# ifdef CHIMERA_LITTLE_ENDIAN + expansion_buffer[0] = red[t & 1]; + expansion_buffer[1] = green[t & 1]; + expansion_buffer[2] = blue[t & 1]; + expansion_buffer[3] = red[t >> 1 & 1]; + expansion_buffer[4] = green[t >> 1 & 1]; + expansion_buffer[5] = blue[t >> 1 & 1]; + expansion_buffer[6] = red[t >> 2 & 1]; + expansion_buffer[7] = green[t >> 2 & 1]; + expansion_buffer[8] = blue[t >> 2 & 1]; + expansion_buffer[9] = red[t >> 3 & 1]; + expansion_buffer[10] = green[t >> 3 & 1]; + expansion_buffer[11] = blue[t >> 3 & 1]; + expansion_buffer[12] = red[t >> 4 & 1]; + expansion_buffer[13] = green[t >> 4 & 1]; + expansion_buffer[14] = blue[t >> 4 & 1]; + expansion_buffer[15] = red[t >> 5 & 1]; + expansion_buffer[16] = green[t >> 5 & 1]; + expansion_buffer[17] = blue[t >> 5 & 1]; + expansion_buffer[18] = red[t >> 6 & 1]; + expansion_buffer[19] = green[t >> 6 & 1]; + expansion_buffer[20] = blue[t >> 6 & 1]; + expansion_buffer[21] = red[t >> 7 & 1]; + expansion_buffer[22] = green[t >> 7 & 1]; + expansion_buffer[23] = blue[t >> 7 & 1]; +# else + expansion_buffer[0] = red[t >> 7 & 1]; + expansion_buffer[1] = green[t >> 7 & 1]; + expansion_buffer[2] = blue[t >> 7 & 1]; + expansion_buffer[3] = red[t >> 6 & 1]; + expansion_buffer[4] = green[t >> 6 & 1]; + expansion_buffer[5] = blue[t >> 6 & 1]; + expansion_buffer[6] = red[t >> 5 & 1]; + expansion_buffer[7] = green[t >> 5 & 1]; + expansion_buffer[8] = blue[t >> 5 & 1]; + expansion_buffer[9] = red[t >> 4 & 1]; + expansion_buffer[10] = green[t >> 4 & 1]; + expansion_buffer[11] = blue[t >> 4 & 1]; + expansion_buffer[12] = red[t >> 3 & 1]; + expansion_buffer[13] = green[t >> 3 & 1]; + expansion_buffer[14] = blue[t >> 3 & 1]; + expansion_buffer[15] = red[t >> 2 & 1]; + expansion_buffer[16] = green[t >> 2 & 1]; + expansion_buffer[17] = blue[t >> 2 & 1]; + expansion_buffer[18] = red[t >> 1 & 1]; + expansion_buffer[19] = green[t >> 1 & 1]; + expansion_buffer[20] = blue[t >> 1 & 1]; + expansion_buffer[21] = red[t & 1]; + expansion_buffer[22] = green[t & 1]; + expansion_buffer[23] = blue[t & 1]; +# endif + expansion_buffer += 24; + } +} + +static void +lf_expand_bitmap_to_gray( + byte *expansion_buffer, + byte *source_buffer, + int width, + Intensity *red_cmap, + Intensity *green_cmap, + Intensity *blue_cmap, + int transparent, + Intensity red_bg, + Intensity green_bg, + Intensity blue_bg) +{ + int i; + byte gray[2]; + gray[0] = red_cmap[0] >> 8; + gray[1] = red_cmap[1] >> 8; + if(transparent != -1) + gray[transparent] = red_bg >> 8; + + for(i = (width + CHAR_BITS - 1) / CHAR_BITS; i; i--) + { + byte t = *source_buffer++; +# ifdef CHIMERA_LITTLE_ENDIAN + expansion_buffer[0] = gray[t & 1]; + expansion_buffer[1] = gray[t >> 1 & 1]; + expansion_buffer[2] = gray[t >> 2 & 1]; + expansion_buffer[3] = gray[t >> 3 & 1]; + expansion_buffer[4] = gray[t >> 4 & 1]; + expansion_buffer[5] = gray[t >> 5 & 1]; + expansion_buffer[6] = gray[t >> 6 & 1]; + expansion_buffer[7] = gray[t >> 7 & 1]; +# else + expansion_buffer[0] = gray[t >> 7 & 1]; + expansion_buffer[1] = gray[t >> 6 & 1]; + expansion_buffer[2] = gray[t >> 5 & 1]; + expansion_buffer[3] = gray[t >> 4 & 1]; + expansion_buffer[4] = gray[t >> 3 & 1]; + expansion_buffer[5] = gray[t >> 2 & 1]; + expansion_buffer[6] = gray[t >> 1 & 1]; + expansion_buffer[7] = gray[t & 1]; +# endif + expansion_buffer += 8; + } +} + + +static void +lf_set_up_expansion(is) +ImageState *is; +{ + if(is->expanded_type == IGRAY) + is->expansion_function = &lf_expand_bitmap_to_gray; + else + is->expansion_function = &lf_expand_bitmap_to_true; +} + +static void +lf_set_up_dither(is) +ImageState *is; +{ + byte *maps[3]; + int i; + cct_8_true_conversion_table cdt; + ImageClass *ic = is->ic; + Image *image = lf_get_image(is); + int image_type; + + is->dithering = false; + + if(is->expansion_buf) + { + if(image->depth > 1) is->expanded_type = image_type = ITRUE; + else image_type = is->expanded_type; /* lf_set_up_bitmap already set it */ + } + else + { + image_type = image->type; + } + + maps[0] = maps[1] = maps[2] = 0; + + if (image->type == IRGB || image->type == IGRAY) + { + maps[0] = lf_get_8_bit_map(is->mp, image->rgb.red); + maps[1] = lf_get_8_bit_map(is->mp, image->rgb.green); + maps[2] = lf_get_8_bit_map(is->mp, image->rgb.blue); + } + + if (image->transparent != -1) + { + maps[0][image->transparent] = is->bgcolor.red >> 8; + maps[1][image->transparent] = is->bgcolor.green >> 8; + maps[2][image->transparent] = is->bgcolor.blue >> 8; + } + + /* + * A real color dither/conversion? + */ + if ((image_type == IRGB || image_type == ITRUE) && ic->colorcube) + { + if (image_type == IRGB && ic->colorcube->cube_type == cube_true_color) + { + /* + * colormap to true-color conversion needed + */ + if (!ic->color_tables[0].true_true_conversion) + { + ic->color_tables[0].true_true_conversion = + ccf_create_true_true_conversion_table(ic->colorcube); + } + cdt = ccf_true_true_to_8_true_conversion_table( + ic->color_tables[0]. + true_true_conversion, + image->rgb.used, + maps); + is->dither_table[0].eight_true_conversion = cdt; + is->dither_table[1].eight_true_conversion = cdt; + is->dither_table[2].eight_true_conversion = cdt; + is->dither_table[3].eight_true_conversion = cdt; + is->free_dither_table = 1; + is->dither_function = &ddf_convert_line_8; + is->dithering = false; + } + else if (image_type == IRGB && + (ic->colorcube->cube_type == cube_mapping || + ic->colorcube->cube_type == cube_no_mapping)) + { + /* + * 8-bit palette to 8-bit screen dither + */ + if (!ic->color_tables[0].true_8_dither) + for (i = 0; i < TCOUNT; i++) + ic->color_tables[i].true_8_dither = + ccf_create_true_8_dither_table(i, 4, 4, ic->colorcube); + for (i = 0; i < TCOUNT; i++) + { + is->dither_table[i].eight_8_dither = + ccf_true_8_to_8_8_dither_table(ic->color_tables[i].true_8_dither, + image->rgb.used, maps); + if(image->transparent != -1) + ccf_set_specially_allocated(is->dither_table[i].eight_8_dither, + image->transparent, + is->bgcolor.pixel); + } + is->free_dither_table = TCOUNT; + is->dither_function = &ddf_dither_line_8; + is->dithering = true; + } + else if (image_type == ITRUE && + ic->colorcube->cube_type == cube_true_color) + { + /* + * true-true conversion job + */ + if (!ic->color_tables[0].true_true_conversion) + ic->color_tables[0].true_true_conversion = + ccf_create_true_true_conversion_table(ic->colorcube); + for (i = 0; i < TCOUNT; i++) + is->dither_table[i].true_true_conversion = + ic->color_tables[0].true_true_conversion; + is->free_dither_table = 0; /* don't free it its a copied pointer!! */ + is->dither_function = &ddf_convert_line_24; + is->dithering = false; + } + else if (image_type == ITRUE && + (ic->colorcube->cube_type == cube_mapping || + ic->colorcube->cube_type == cube_no_mapping)) + { + /* + * true-color to 8-bit dither-type-situation + */ + if (!ic->color_tables[0].true_8_dither) + for (i = 0; i < TCOUNT; i++) + ic->color_tables[i].true_8_dither = + ccf_create_true_8_dither_table(i, 4, 4, ic->colorcube); + for (i = 0; i < TCOUNT; i++) + is->dither_table[i].true_8_dither = + ic->color_tables[i].true_8_dither; + is->free_dither_table = 0; /* don't free it its a copied pointer!! */ + is->dither_function = &ddf_color_dither_line_24; + is->dithering = true; + } + } + /* + * else a black and white dither + */ + else if ((image_type == IRGB || image_type == ITRUE || + image_type == IGRAY) && + ic->grayscale && ic->grayscale->u.grayscale.value_count < 29) + { + if (!ic->gray_tables[0].eight_8_dither) + for (i = 0; i < TCOUNT; i++) + ic->gray_tables[i].eight_8_dither = + ccf_create_gray_dither_table(i, 4, 4, ic->grayscale); + if(is->depth == 1) + { + /* 1-bit images need special handling because they are not + displayed using the colormap in an XPutImage. Instead the + Graphics context foreground and background colors are used */ + if(ic->grayscale->u.grayscale.pixel_values[1]) + { + is->fg = ic->grayscale->u.grayscale.pixel_values[1]; + is->bg = ic->grayscale->u.grayscale.pixel_values[0]; + } + else + { + is->fg = ic->grayscale->u.grayscale.pixel_values[0]; + is->bg = ic->grayscale->u.grayscale.pixel_values[1]; + } + } + if (image_type == IRGB || image_type == IGRAY) + { + /* + * colormap to grayscale dither + */ + if (lf_null_gray_map(maps) && image->transparent == -1) + { + for (i = 0; i < TCOUNT; i++) + is->dither_table[i].eight_8_dither = + ic->gray_tables[i].eight_8_dither; + is->free_dither_table = 0; /* don't free it its a copied pointer */ + } + else + { + for (i = 0; i < TCOUNT; i++) + { + is->dither_table[i].eight_8_dither = + ccf_gray_to_gray_dither_convert(ic->gray_tables[i]. + eight_8_dither, + image->rgb.used, + maps); + if(image->transparent != -1) + ccf_set_specially_allocated(is->dither_table[i].eight_8_dither, + image->transparent, + is->bgcolor.pixel); + + } + is->free_dither_table = 4; + } + is->dither_function = &ddf_dither_line_8; + is->dithering = true; + } + else + { + /* + * True-color-to-grayscale type situtation + */ + for (i = 0; i < TCOUNT; i++) + is->dither_table[i].true_8_dither = + ic->gray_tables[i].true_8_dither; + is->free_dither_table = 0; /* don't free it its a copied pointer!! */ + is->dither_function = &ddf_gray_dither_line_24; + is->dithering = true; + } + } + /* + * else a bw conversion + */ + else if ((image_type == IRGB || image_type == ITRUE || + image_type == IGRAY) && + ic->grayscale && + ic->grayscale->u.grayscale.value_count >= 29) + { + if (image->transparent != -1 && + (image_type == IRGB || image_type == IGRAY)) + { + maps[0][image->transparent] = is->bgcolor.red >> 8; + maps[1][image->transparent] = is->bgcolor.green >> 8; + maps[2][image->transparent] = is->bgcolor.blue >> 8; + } + + if (!ic->gray_tables[0].eight_true_conversion) + ic->gray_tables[0].eight_true_conversion = + ccf_create_gray_conversion_table(ic->grayscale); + if (image_type == IGRAY || image_type == IRGB) + { + /* + * Simple remap of input values to output values + */ + if (lf_null_gray_map(maps)) + { + for (i = 0; i < TCOUNT; i++) + is->dither_table[i].eight_true_conversion = + ic->gray_tables[0].eight_true_conversion; + is->free_dither_table = 0; /* don't free it its a copied pointer */ + } + else + { + for (i = 0; i < TCOUNT; i++) + is->dither_table[i].eight_true_conversion = + ccf_gray_to_gray_conversion_convert(ic->gray_tables[0]. + eight_true_conversion, + image->rgb.used, + maps); + is->free_dither_table = TCOUNT; + } + is->dither_function = &ddf_convert_line_8; + is->dithering = false; + } + else + { + /* + * Reduce 24-bit color to grayscale without dithering + */ + for (i = 0; i < TCOUNT; i++) + is->dither_table[i].eight_true_conversion = + ic->gray_tables[0].eight_true_conversion; + is->free_dither_table = 0; /* don't free it its a copied pointer */ + is->dither_function = &ddf_gray_convert_line_24; + is->dithering = true; + } + } + + maps[0] = maps[1] = maps[2] = 0; + + return; +} + + +/* + * I'm going to need this soon: + + *special_return = + cube_table.true_true_conversion->pixel_values[0][intensities[0] >> 8] + + cube_table.true_true_conversion->pixel_values[1][intensities[1] >> 8] + + cube_table.true_true_conversion->pixel_values[2][intensities[2] >> 8]; + * + */ + + +static void +lf_get_new_specials(is, ic, image, input) +ImageState *is; +ImageClass *ic; +Image *image; +byte *input; +{ + bool *blacklist = is->special_blacklist; + bool *real_blacklist = is->special_real_blacklist; + /* + * Find 3 identical pixels and win a new special color allocation. The + * pixels are searched for spaced 4 apart, because this ensures that + * they are not just antialias pixels on the edge of writing or other + * unimportantly strewn pixels. Also, this catches the case where the + * GIF has already beed dithered with a 4x4 matrix. This is an important + * case because it can interfere with our own dithering and produce + * ugly artefacts. + */ + if (!is->expansion_buf && + is->dithering && + image->type == IRGB && + !is->no_more_specials) + { + int i; + for(i = 0; i < image->width - 8; i++, input++) + { + byte t = *input; + bool dont_really_allocate; + + if (real_blacklist[t]) continue; + + if (ic->special_count < ic->special_max && + is->image_special_count < is->image_special_max && + t == input[4] && + t == input[8]) + { + real_blacklist[t] = true; + dont_really_allocate = false; + } + else + { + if(blacklist[t]) continue; + dont_really_allocate = true; + } + blacklist[t] = true; + { + Intensity intensities[3]; + bool really_allocated; + unsigned long special_entry; + + if(t == image->transparent) continue; + + intensities[0] = image->rgb.red[t]; + intensities[1] = image->rgb.green[t]; + intensities[2] = image->rgb.blue[t]; + if (!xccf_allocate_special(is->dpy, + is->cmap, + ic->colorcube, + ic->grayscale, + intensities, + &really_allocated, + dont_really_allocate, + ic->special_colors, + ic->special_count, + &special_entry)) + { + /* + * If a color allocation fails once, we won't bother the + * X server again. Color allocation is a slow round trip + */ + if (!dont_really_allocate) + is->image_special_max = is->image_special_count; + } + else + { + int j; + if(really_allocated) + { + ic->special_count++; + is->image_special_count++; + if(is->image_special_count >= image->rgb.used) + is->no_more_specials = true; + for(j = 0; j < 256; j++) + blacklist[j] = false; + } + for(j = 0; j < TCOUNT; j++) + { + ccf_set_specially_allocated(is->dither_table[j].eight_8_dither, + t, special_entry); + } + } + } + } + } +} + +void +SetSize(is) +ImageState *is; +{ + unsigned int fw, fh; + XImage *xi = is->xi; + bool sbstate; + + if (GUIGetDimensions(is->wd, &fw, &fh) == -1) + { + GUISetScrollBar(is->wd, false); + GUISetInitialDimensions(is->wd, xi->width, xi->height); + GUISetDimensions(is->wd, xi->width, xi->height); + } + else + { + if (xi->width > fw || xi->height > fh) sbstate = true; + else sbstate = false; + GUISetScrollBar(is->wd, sbstate); + GUISetDimensions(is->wd, xi->width, xi->height); + } + + return; +} + +static void +ImageToXImage(pointer, fline, lline) +void *pointer; +int fline; +int lline; +{ + ImageState *is = (ImageState *)pointer; + ImageClass *ic = is->ic; + byte *input; + char *output; + int depth; + int line; + Image *image = lf_get_image(is); + int image_type = image->type; + + /* + * First time + */ + if (is->xi == NULL) + { + /* + * No image gets more than half the special colors + */ + is->image_special_max = (ic->special_max - ic->special_count) / 2; + + if (image->pixlen == 1) depth = 1; + else depth = is->depth; + + if(image->depth == 1) + { + lf_set_up_bitmap(is); + if(is->expansion_buf) lf_set_up_expansion(is); + image_type = is->expanded_type; + } + else + { + is->expanded_type = image->type; + } + + if (is->expansion_buf) depth = is->depth; + + if (image_type == IRGB || image_type == ITRUE) + { + if (!ic->colorcube) + { + ic->colorcube = + xccf_allocate_cube(is->dpy, is->cmap, is->v, is->depth); + } + } + if ((image_type == IGRAY || !ic->colorcube) && image->pixlen != 1) + { + if (!ic->grayscale) + { + ic->grayscale = + xccf_allocate_grays(is->dpy, is->cmap, is->v, is->depth); + } + } + + is->xi = XCreateImage(is->dpy, is->v, + depth, + depth == 1 ? XYBitmap : ZPixmap, + 0, NULL, + image->width, image->height, + 32, 0); + + /* Make sure we have plenty of padding at the end of each line */ + is->xi->bytes_per_line += 16; + is->xi->data = (char *)MPCGet(is->mp, + image->height * is->xi->bytes_per_line); + +#ifdef CHIMERA_LITTLE_ENDIAN + is->xi->byte_order = is->xi->bitmap_bit_order = LSBFirst; +#else + is->xi->byte_order = is->xi->bitmap_bit_order = MSBFirst; +#endif + + if(depth != 1) XAddPixel(is->xi, is->bgcolor.pixel); + if(image->depth != 1 || is->expansion_buf) lf_set_up_dither(is); + + SetSize(is); + } + + /* + * Every time + */ + input = image->data + image->bytes_per_line * fline; + output = is->xi->data + is->xi->bytes_per_line * fline; + +/* + lf_get_new_specials(is, ic, image, input); +*/ + + for (line = fline; + line <= lline; + line++, input += image->bytes_per_line, + output += is->xi->bytes_per_line) + { + if (image->pixlen == 1 && !is->expansion_buf) + { + memcpy(output, input, + MIN(is->xi->bytes_per_line, image->bytes_per_line)); + } + else + { + byte *expanded_input; + if (is->expansion_buf) + { + is->expansion_function(is->expansion_buf, + input, + image->width, + image->rgb.red, + image->rgb.green, + image->rgb.blue, + image->transparent, + (Intensity)is->bgcolor.red, + (Intensity)is->bgcolor.green, + (Intensity)is->bgcolor.blue); + expanded_input = is->expansion_buf; + } + else + { + expanded_input = input; + } + if(is->dither_function) + { + is->dither_function(is->dither_table[line & 3], + expanded_input, + output, + is->xi->bits_per_pixel, + image->width); + } + else + { + printf("Warning:no dither function defined for image\n"); + } + } + } + + /* + * Paint line + */ + if (is->xi->depth == 1) + { + XSetForeground(is->dpy, ic->gc, is->fg); + XSetBackground(is->dpy, ic->gc, is->bg); + } + XPutImage(is->dpy, is->win, ic->gc, is->xi, + 0, fline, + 0, fline, + is->xi->width, lline - fline + 1); + + if (lline > is->last_line) is->last_line = lline; + + return; +} + +/* + * ImageClassInit + */ +static void +ImageClassInit(ic, dpy) +ImageClass *ic; +Display *dpy; +{ +/* XGCValues xgcv; */ + + ic->icount = 0; + ic->init = True; + ic->gc = XCreateGC(dpy, RootWindow(dpy, DefaultScreen(dpy)), 0, NULL); + + if (getenv("CHIMERA_MAX_SPECIAL_COLORS")) + { + ic->special_max = atoi(getenv("CHIMERA_MAX_SPECIAL_COLORS")); + } + else + { + ic->special_max = 64; + } + + return; +} + +/* + * ImageDestroy + */ +static void +ImageDestroy(closure) +void *closure; +{ + ImageState *is = (ImageState *)closure; + int i; + + if (is->if_vector.destroyProc && is->if_vector.image_format_closure != NULL) + { + is->if_vector.destroyProc(is->if_vector.image_format_closure); + is->if_vector.image_format_closure = NULL; + } + if(is->xi) + { + is->xi->data = NULL; + XDestroyImage(is->xi); + is->xi = 0; + } + for(i = 0; i < is->free_dither_table; i++) + { + if(is->dither_table[i].generic_dither_table) + free_mem(is->dither_table[i].generic_dither_table); + is->dither_table[i].generic_dither_table = 0; + } + for( ; i < TCOUNT; i++) + is->dither_table[i].generic_dither_table = 0; + is->free_dither_table = 0; + + if (--is->ic->icount == 0) + { + /* free specially allocated colors here? */ + } + + MPDestroy(is->mp); + + return; +} + +/* + * ImageInit + * + * This is where the initialization for a frame begins. It is + * called when the content-type becomes known. + */ +void * +ImageInit(wn, class_closure, state) +ChimeraRender wn; +void *class_closure; +void *state; /* ignored */ +{ + ImageClass *ic = (ImageClass *)class_closure; + ImageState *is; + int format; + XWindowAttributes xwa; + int status; + char *content; + int i; + ChimeraSink wp; + ChimeraGUI wd; + MemPool mp; + + wp = RenderToSink(wn); + wd = RenderToGUI(wn); + + content = SinkGetInfo(wp, "content-type"); + + for (i = 0; content_map[i].name != NULL; i++) + { + if (strcasecmp(content_map[i].name, content) == 0) break; + } + if (content_map[i].name == NULL) return(NULL); + format = content_map[i].id; + + mp = MPCreate(); + is = (ImageState *)MPCGet(mp, sizeof(ImageState)); + is->mp = mp; + + is->win = GUIToWindow(wd); + is->dpy = GUIToDisplay(wd); + + if (!ic->init) ImageClassInit(ic, is->dpy); + + status = XGetWindowAttributes(is->dpy, is->win, &xwa); + is->v = xwa.visual; + is->depth = xwa.depth; + is->cmap = xwa.colormap; + is->ic = ic; + is->wd = wd; + is->wp = wp; + is->bgcolor.pixel = GUIBackgroundPixel(wd); + XQueryColor(is->dpy, is->cmap, &(is->bgcolor)); + + if (format == IMAGE_GIF) gifInit(ImageToXImage, is, &is->if_vector); + else if (format == IMAGE_PNM) pnmInit(ImageToXImage, is, &is->if_vector); + else if (format == IMAGE_XBM) xbmInit(ImageToXImage, is, &is->if_vector); +#ifdef HAVE_JPEG + else if (format == IMAGE_JPEG) jpegInit(ImageToXImage, is, &is->if_vector); +#endif +#ifdef HAVE_PNG + else if (format == IMAGE_PNG) pngInit(ImageToXImage, is, &is->if_vector); +#endif + + ic->icount++; + + return(is); +} + +/* + * ImageAdd + */ +static void +ImageAdd(closure) +void *closure; +{ + ImageState *is = (ImageState *)closure; + byte *data; + size_t len; + MIMEHeader mh; + + SinkGetData(is->wp, &data, &len, &mh); + + if (is->if_vector.addDataProc && is->if_vector.image_format_closure) + { + is->if_vector.addDataProc(is->if_vector.image_format_closure, + data, len, false); + } + + return; +} + +/* + * ImageEnd + */ +static void +ImageEnd(closure) +void *closure; +{ + ImageState *is = (ImageState *)closure; + byte *data; + size_t len; + MIMEHeader mh; + + SinkGetData(is->wp, &data, &len, &mh); + + if (is->if_vector.addDataProc && is->if_vector.image_format_closure) + { + is->if_vector.addDataProc(is->if_vector.image_format_closure, + data, len, true); + } + + return; +} + +static bool +ImageExpose(closure, ex, ey, ewidth, eheight) +void *closure; +int ex, ey; +unsigned int ewidth, eheight; +{ + ImageState *is = (ImageState *)closure; + unsigned int height; + + if (is->xi == NULL) return(true); + + if (ey > is->last_line) return(true); + + if (ey + eheight > is->last_line) height = is->last_line - ey; + else height = eheight; + + XPutImage(is->dpy, is->win, is->ic->gc, is->xi, + ex, ey, + ex, ey, + ewidth, height + 1); + + return(true); +} + +static void +ImageClassDestroy(closure) +void *closure; +{ + ImageClass *ic = (ImageClass *)closure; + int i; + + ic->refcount--; + if (ic->refcount > 0) return; + + free_mem(ic->colorcube); + free_mem(ic->grayscale); + for (i = 0; i < TCOUNT; i++) + { + if (ic->color_tables[i].generic_dither_table != NULL) + { + free_mem(ic->color_tables[i].generic_dither_table); + } + } + for (i = 0; i < TCOUNT; i++) + { + if (ic->gray_tables[i].generic_dither_table != NULL) + { + free_mem(ic->gray_tables[i].generic_dither_table); + } + } + MPDestroy(ic->mp); + + return; +} + +static void +ImageCancel(closure) +void *closure; +{ + return; +} + +int +InitModule_Image(cres) +ChimeraResources cres; +{ + ChimeraRenderHooks rh; + ImageClass *ic; + int i; + MemPool mp; + + mp = MPCreate(); + ic = (ImageClass *)MPCGet(mp, sizeof(ImageClass)); + ic->mp = mp; + + for (i = 0; content_map[i].name != NULL; i++) + { + memset(&rh, 0, sizeof(ChimeraRenderHooks)); + rh.content = content_map[i].name; + rh.class_context = ic; + ic->refcount++; + rh.class_destroy = ImageClassDestroy; + rh.init = ImageInit; + rh.add = ImageAdd; + rh.end = ImageEnd; + rh.destroy = ImageDestroy; + rh.cancel = ImageCancel; + rh.expose = ImageExpose; + RenderAddHooks(cres, &rh); + } + + return(0); +} diff --git a/image/image_endian.h b/image/image_endian.h new file mode 100644 index 0000000..69a0957 --- /dev/null +++ b/image/image_endian.h @@ -0,0 +1,82 @@ +/* + * endian.h + */ + +#ifndef __ENDIAN_H__ +#define __ENDIAN_H__ 1 + +/* + * Endianism determination by Erik Corry. Please email changes/additions!!! + */ + +#if !defined(__MIPSEL__) && (defined(MIPSEL) || defined(__MIPSEL) || defined(__MIPSEL__) || defined(__mipsel) || defined(__mipsel__)) +#define __MIPSEL__ 1 +#endif + +#if !defined(__MIPSEB__) && (defined(MIPSEB) || defined(__MIPSEB) || defined(__MIPSEB__) || defined(__mipseb) || defined(__mipseb__) || defined(_MIPSEB)) +#define __MIPSEB__ 1 +#endif + +#if !defined(__SPARC__) && (defined(SPARC) || defined(__SPARC) || defined(__SPARC__) || defined(__sparc) || defined(__sparc__)) +#define __SPARC__ 1 +#endif + +#if !defined(__alpha__) && (defined(ALPHA) || defined(__ALPHA) || defined(__ALPHA__) || defined(__alpha)) +#define __alpha__ 1 +#endif + +#if !defined(__680x0__) && (defined(__680x0) || defined(__680x0__) || defined(__mc68000__)) +#define __680x0__ 1 +#endif + +#if !defined(__AIX__) && (defined(AIX) || defined(_AIX) || defined(__AIX) || defined(__AIX__)) +#define __AIX__ 1 +#endif + +#if !defined(__RS6000__) && (defined(__AIX__) || defined(RS6000) || defined(_RS6000) || defined(__RS6000) || defined(__RS6000__)) +#define __RS6000__ 1 +#endif + +#if !defined(__HPUX__) && (defined(HPUX) || defined(_HPUX) || defined(__HPUX) || defined(__HPUX__)) +#define __HPUX__ 1 +#endif +#if !defined(__HPUX__) && (defined(hpux) || defined(_hpux) || defined(__hpux) || defined(__hpux__)) +#define __HPUX__ 1 +#endif + +#if !defined(__VAX__) && (defined(VAX) || defined (__VAX)) +#define __VAX__ 1 +#endif + +#if defined(__i386__) || defined(__VAX__) || defined(__MIPSEL__) || defined(__alpha__) || defined(__QNX__) +#undef CHIMERA_BIG_ENDIAN +#define CHIMERA_LITTLE_ENDIAN +#endif + +#if defined(__RS6000__) || defined(__SPARC__) || defined(__680x0__) || defined(__HPUX__) || defined(__MIPSEB__) || defined(__convex__) +#undef CHIMERA_LITTLE_ENDIAN +#define CHIMERA_BIG_ENDIAN +#endif + +#if !defined(CHIMERA_LITTLE_ENDIAN) && !defined(CHIMERA_BIG_ENDIAN) +/* + * If you hit this and you don't know what symbols your system defines, + * look in the compiler manual. Try to find a symbol that identifies the + * processor, rather than the OS or compiler. If you have gcc on a Unix + * system, the following will tell you what symbols it defines: + * + * ln -s /dev/null null.c + * gcc -ansi -dM -E null.c + * + * If you have a system that does not have a clear integer endianism you + * are going to have severe portability problems. + * + */ +Error: Unknown endianism of architecture +#endif + +#ifdef __alpha__ +#define SIXTYFOUR_BIT +#endif + +#endif /* __ENDIAN_H__ */ diff --git a/image/image_format.h b/image/image_format.h new file mode 100644 index 0000000..47f90d6 --- /dev/null +++ b/image/image_format.h @@ -0,0 +1,56 @@ +/* + * image_format.h + * + * Copyright (c) 1995 Erik Corry ehcorry@inet.uni-c.dk + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef IMAGE_FORMAT_H_INCLUDED +#define IMAGE_FORMAT_H_INCLUDED + +typedef struct ifs_vector *ift_vector; + +typedef void (*FormatLineProc) _ArgProto((void *, int, int)); + +typedef void (InitProcDecl) _ArgProto((FormatLineProc line_proc, + void *line_proc_closure, + ift_vector vector)); +typedef InitProcDecl *InitProc; +typedef void (*DestroyProc) _ArgProto((void *image_format_closure)); +typedef int (*AddDataProc) _ArgProto((void *image_format_closure, + byte *data, + int len, + bool data_ended)); + +typedef Image *(*GetImageProc) _ArgProto((void *image_format_closure)); + +InitProcDecl gifInit; +InitProcDecl xbmInit; +InitProcDecl pnmInit; +InitProcDecl jpegInit; +InitProcDecl pngInit; + +struct ifs_vector +{ + void *image_format_closure; + int image_format; + InitProc initProc; + DestroyProc destroyProc; + AddDataProc addDataProc; + GetImageProc getImageProc; +}; + +#endif diff --git a/image/imagep.h b/image/imagep.h new file mode 100644 index 0000000..305b116 --- /dev/null +++ b/image/imagep.h @@ -0,0 +1,106 @@ +/* image.h: + * + * portable image type declarations + * + * jim frost 10.02.89 + * + * Copyright 1989 Jim Frost. + * + * Permission to use, copy, modify, distribute, and sell this software + * and its documentation for any purpose is hereby granted without fee, + * provided that the above copyright notice appear in all copies and + * that both that copyright notice and this permission notice appear + * in supporting documentation. The author makes no representations + * about the suitability of this software for any purpose. It is + * provided "as is" without express or implied warranty. + * + * THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS + * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE + * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE + * USE OR PERFORMANCE OF THIS SOFTWARE. + * + * Changes (c) Copyright 1995 Erik Corry ehcorry@inet.uni-c.dk + */ + +#ifndef __IMAGEP_H__ +#define __IMAGEP_H__ 1 + +#ifndef MAX +#define MAX(a,b) ((a) < (b) ? (b) : (a)) +#endif + +#ifndef MIN +#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#endif + +/* + * should be in limits.h + */ +#ifndef CHAR_BITS +#define CHAR_BITS 8 +#endif + +typedef unsigned short Intensity; + +typedef struct rgbcolor +{ + Intensity red, green, blue; +} RGBColor; + +typedef struct rgbmap +{ + unsigned int size; /* size of RGB map (bytes per enty) */ + unsigned int used; /* number of colors used in RGB map */ + unsigned int compressed; /* image uses colormap fully */ + Intensity *red; /* color values in X style */ + Intensity *green; + Intensity *blue; +} RGBMap; + +/* image structure + */ + +typedef struct +{ + unsigned int type; /* type of image */ + int x, y; /* x, y of decoded image */ + int pass; /* interlace pass */ + int transparent; /* transparent color index */ + RGBMap rgb; /* RGB map of image if IRGB or IGRAY type */ + unsigned int width; /* width of image in pixels */ + unsigned int height; /* height of image in pixels */ + unsigned int depth; /* depth of image in bits */ + unsigned int pixlen; /* length of pixel in bits after padding */ + unsigned int bytes_per_line; /* After padding */ + float gamma; /* gamma of display the image is adjusted for */ + byte *data; /* data rounded to full byte for each row */ +} Image; + +#define IBAD 0 /* invalid image type (used when freeing) */ +#define IBITMAP 1 /* image is a bitmap */ +#define IRGB 2 /* image is RGB */ +#define ITRUE 3 /* image is true color */ +#define IGRAY 4 /* image is gray scale */ + +/* new.c */ +Image *newBitImage _ArgProto((unsigned int width, unsigned int height)); +Image *newTrueImage _ArgProto((unsigned int width, unsigned int height)); +Image *newRGBImage _ArgProto((unsigned int width, unsigned int height, + unsigned int depth)); +void freeImage _ArgProto((Image *image)); + +/* + * this returns the (approximate) intensity of an RGB triple + */ + +#define colorIntensity(R,G,B) \ + (RedIntensity[(R) >> 8] + GreenIntensity[(G) >> 8] + BlueIntensity[(B) >> 8]) + +extern unsigned short RedIntensity[]; +extern unsigned short GreenIntensity[]; +extern unsigned short BlueIntensity[]; + +#endif /* __IMAGEP_H__ */ diff --git a/image/jpeg.c b/image/jpeg.c new file mode 100644 index 0000000..ef266c1 --- /dev/null +++ b/image/jpeg.c @@ -0,0 +1,570 @@ +/* + * jpeg.c: + * + * (c) 1995 Erik Corry ehcorry@inet.uni-c.dk + * + * modelled on xbm.c. + * + * If you want to understand this read the following files from libjpeg: + * + * libjpeg.doc + * jdatasrc.c + * jpeglib.h + * example.c + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +#include "port_before.h" + +#include +#include +#include + +#include "port_after.h" + +#include "common.h" +#include "imagep.h" + +#include "jpegp.h" + +static void jpegDestroy _ArgProto((void *)); + +/* + * jpegGetImage - called through vector + */ + +static Image * +jpegGetImage(void *pointer) +{ + jpegState *jpeg = (jpegState *)pointer; + return jpeg->image; +} + +/* + * jpegDestroy - called through vector + */ +static void +jpegDestroy(pointer) +void *pointer; +{ + jpegState *jpeg = (jpegState *)pointer; + if (jpeg) { + if(setjmp(jpeg->error_state.jump_buffer)) + { + /* + * Oh shit, an error while clearing up... + */ + fprintf(stderr, "jpegDestroy: failed\n"); + return; + } + if (jpeg->image) + freeImage(jpeg->image); + jpeg->image = 0; + if (jpeg->destroy_jpeg) + jpeg_destroy_decompress(&jpeg->cinfo); + jpeg->destroy_jpeg = false; + free_mem(jpeg); + } +} + +/* + * these routines form a suspending "data source manager" for libjpeg. + */ + +static void +lf_init_source_callback( + struct jpeg_decompress_struct *cinfo) +{ +} + +static boolean +lf_fill_input_buffer_callback( + struct jpeg_decompress_struct *cinfo) +{ + struct jpeg_chimera_input_state *input_state = + (struct jpeg_chimera_input_state *)(cinfo->src); + + if (! input_state->at_eof) + return FALSE; /* force suspension until more data arrives */ + + /* If libjpeg demands more data beyond the EOF, + * pacify it by supplying EOI marker(s). + * This lets us produce some kind of image from a trucated or corrupted file. + */ + + WARNMS(cinfo, JWRN_JPEG_EOF); + + input_state->fake_eoi[0] = (JOCTET) 0xFF; + input_state->fake_eoi[1] = (JOCTET) JPEG_EOI; + input_state->pub.next_input_byte = input_state->fake_eoi; + input_state->pub.bytes_in_buffer = 2; + input_state->faked_eoi = true; + + return TRUE; +} + +static void +lf_skip_input_data_callback( + struct jpeg_decompress_struct *cinfo, + long num_bytes) +{ + struct jpeg_chimera_input_state *input_state = + (struct jpeg_chimera_input_state *)(cinfo->src); + + if (num_bytes <= 0) return; + + if (num_bytes > (long) input_state->pub.bytes_in_buffer) + { + /* Tricky code here: we advance next_input_byte beyond the end of the + * buffer. libjpeg will suspend because we set bytes_in_buffer to 0, + * and then jpegAddData's bookkeeping will do the right thing. + * This could fail on sufficiently weird architectures, because + * pointers aren't guaranteed by the C standard to be able to point + * past the end of allocated memory; but in practice it should be fine. + */ + input_state->pub.next_input_byte += num_bytes; + input_state->pub.bytes_in_buffer = 0; + } + else + { + input_state->pub.next_input_byte += num_bytes; + input_state->pub.bytes_in_buffer -= num_bytes; + } +} + +static void +lf_term_source_callback( + struct jpeg_decompress_struct *cinfo) +{ +} + +/* + * this routine overrides libjpeg's default fatal-error handler. + */ + +static void +lf_error_exit_callback( + j_common_ptr cinfo) +{ + struct jpeg_chimera_error_state *error_state = + (struct jpeg_chimera_error_state *)(cinfo->err); + /* + * Display message. At the moment I use the builtin stderr handler, + * but we could install something smarter, or just keep quiet about it. + * Note that if we did want to keep quiet, we'd have to override + * output_message as well as error_exit, so as to suppress warning msgs. + */ + (error_state->pub.output_message)(cinfo); + + /* Return control to the setjmp point */ + longjmp(error_state->jump_buffer, 1); +} + +/* + * These routines are the elements of a state machine for reading JPEGs. + * We use a distinct state for each point at which we may have to suspend + * processing. + */ + +static int +lf_read_header(jpegState *jpeg) +{ + int status = jpeg_read_header(&jpeg->cinfo, TRUE); + + if (status == JPEG_SUSPENDED) return CH_JPEG_NEED_DATA; + + /* + * Done reading header; set decompression parameters. + * Probably ought to have some user-settable preferences here. + */ + + /* These are needed for progressive rendering */ + if (jpeg_has_multiple_scans(&jpeg->cinfo)) + jpeg->cinfo.buffered_image = TRUE; + jpeg->cinfo.do_block_smoothing = TRUE; + /* These are optional tradeoffs of quality for speed */ + /* if (force-grayscale) jpeg->cinfo.out_color_space = JCS_GRAYSCALE; */ + jpeg->cinfo.dct_method = JDCT_FASTEST; + jpeg->cinfo.do_fancy_upsampling = FALSE; + + /* Advance to next state */ + jpeg->state = CH_JPEG_START_DECOMPRESS; + return CH_JPEG_SUCCESS; +} + +static int +lf_start_decompress(jpegState *jpeg) +{ + int status = jpeg_start_decompress(&jpeg->cinfo); + + if (status == FALSE) return CH_JPEG_NEED_DATA; + + /* + * start_decompress done; get image parameters + */ + + if (jpeg->cinfo.out_color_space == JCS_GRAYSCALE && + jpeg->cinfo.output_components == 1) + { + /* we represent grayscale as RGBI with a gray-ramp palette */ + int i; + jpeg->image = newRGBImage(jpeg->cinfo.output_width, + jpeg->cinfo.output_height, 8); + if (!jpeg->image) + return CH_JPEG_ERROR; + + jpeg->image->type = IGRAY; + jpeg->image->rgb.red = jpeg->cmap; + jpeg->image->rgb.green = jpeg->cmap; + jpeg->image->rgb.blue = jpeg->cmap; + jpeg->image->rgb.used = 256; + for (i = 0; i < 256; i++) + jpeg->cmap[i] = i | (i << 8); + } + else if (jpeg->cinfo.out_color_space == JCS_RGB && + jpeg->cinfo.output_components == 3) + { + jpeg->image = newTrueImage(jpeg->cinfo.output_width, + jpeg->cinfo.output_height); + if (!jpeg->image) + return CH_JPEG_ERROR; + } + else + { + fprintf(stderr, "Unsupported JPEG colorspace\n"); + return CH_JPEG_ERROR; + } + + /* Advance to next state */ + jpeg->state = CH_JPEG_START_OUTPUT; + return CH_JPEG_SUCCESS; +} + +static int +lf_start_output(jpegState *jpeg) +{ + if (jpeg->cinfo.buffered_image) + { + /* Eat all the available data before issuing start_output. + * This ensures we have an up-to-date target scan number + * and prevents executing unnecessary output passes. + * Once we get a SUSPENDED return (or possibly an EOI), + * we can proceed with display. + */ + int status; + for (;;) { + status = jpeg_consume_input(&jpeg->cinfo); + if (status == JPEG_SUSPENDED || status == JPEG_REACHED_EOI) + break; + } + status = jpeg_start_output(&jpeg->cinfo, jpeg->cinfo.input_scan_number); + if (status == FALSE) return CH_JPEG_NEED_DATA; + /* printf("scan %d\n", jpeg->cinfo.input_scan_number); */ + } + + jpeg->ypos = 0; + + /* Advance to next state */ + jpeg->state = CH_JPEG_READ_IMAGE; + return CH_JPEG_SUCCESS; +} + +static int +lf_read_image(jpegState *jpeg) +{ + int scanline_count, max_scanlines; + int i; + unsigned char *scanline_pointers[4]; + unsigned char *output_addr; + + if (jpeg->cinfo.buffered_image) + { + /* In progressive mode, consume all available input right away. + * NOTE: maybe ought to be less greedy if whole file is available??? + */ + for (;;) { + int status = jpeg_consume_input(&jpeg->cinfo); + if (status == JPEG_SUSPENDED || status == JPEG_REACHED_EOI) + break; + } + } + + while (jpeg->ypos < jpeg->cinfo.output_height) + { + /* Somewhat optimistically, we assume that libjpeg may be able to + * give us as many as four scanlines per call. + */ + max_scanlines = MIN(4, jpeg->cinfo.output_height - jpeg->ypos); + output_addr = + jpeg->image->data + jpeg->image->bytes_per_line * jpeg->ypos; + for (i = 0; i < max_scanlines; i++) + { + scanline_pointers[i] = output_addr; + output_addr += jpeg->image->bytes_per_line; + } + + scanline_count = + jpeg_read_scanlines(&jpeg->cinfo, scanline_pointers, max_scanlines); + + if (scanline_count == 0) return CH_JPEG_NEED_DATA; + + if (jpeg->lineProc != NULL) + (jpeg->lineProc)(jpeg->lineClosure, jpeg->ypos, + jpeg->ypos + scanline_count - 1); + + jpeg->ypos += scanline_count; + } + + /* Advance to next state */ + jpeg->state = CH_JPEG_FINISH_OUTPUT; + return CH_JPEG_SUCCESS; +} + +static int +lf_finish_output(jpegState *jpeg) +{ + if (jpeg->cinfo.buffered_image) + { + int status = jpeg_finish_output(&jpeg->cinfo); + if (status == FALSE) return CH_JPEG_NEED_DATA; + + /* We need another output scan unless the input file is all read + * and the just-completed output scan was started after the final + * input scan began arriving. + */ + if (! jpeg_input_complete(&jpeg->cinfo) || + jpeg->cinfo.output_scan_number < jpeg->cinfo.input_scan_number) + { + jpeg->state = CH_JPEG_START_OUTPUT; + return CH_JPEG_SUCCESS; + } + } + + /* Advance to next state */ + jpeg->state = CH_JPEG_FINISH_DECOMPRESS; + return CH_JPEG_SUCCESS; +} + +static int +lf_finish_decompress(jpegState *jpeg) +{ + int status = jpeg_finish_decompress(&jpeg->cinfo); + + if (status == FALSE) return CH_JPEG_NEED_DATA; + + /* Advance to next state */ + jpeg->state = CH_JPEG_FINISHED; + return CH_JPEG_SUCCESS; +} + +/* + * jpegAddData - called through vector + * + * Returns: + * 0 success + * 1 need more data + * -1 error + * + * Assumes data is the address of the beginning of the jpeg data and len + * is the total length. + */ +static int +jpegAddData(pointer, data, len, data_ended) +void *pointer; +byte *data; +int len; +bool data_ended; +{ + jpegState *jpeg = (jpegState *)pointer; + int rval; + +/* + fprintf(stderr, "jpegAddData %p: buf %p %d %d\n", + &jpeg->cinfo, data, len, data_ended); +*/ + + if (setjmp(jpeg->error_state.jump_buffer)) + { + /* Failed. Assume we will get a jpegDestroy call */ + return -1; + } + + /* + * Can't progress unless more data is available (or EOI is reached). + * Note that len < bytes_consumed is entirely valid, if libjpeg has + * commanded a skip beyond the data so far received. + */ + if (len <= jpeg->input_state.bytes_consumed) + { + if (! data_ended) return 1; /* suspend */ + /* If reached EOF, terminate any incomplete skip. */ + jpeg->input_state.bytes_consumed = len; + } + + /* Update libjpeg's input pointers */ + jpeg->input_state.pub.next_input_byte = + ((JOCTET*) data) + jpeg->input_state.bytes_consumed; + jpeg->input_state.pub.bytes_in_buffer = + len - jpeg->input_state.bytes_consumed; + jpeg->input_state.faked_eoi = false; /* next_input_byte is valid */ + if (data_ended) + jpeg->input_state.at_eof = true; + + /* + * Don't bother to parse less than a few hundred bytes at a time; + * this prevents excess cycling in libjpeg. (Normal TCP connections + * will deliver a packet at a time, so this test will probably never + * trigger...) + */ + if (!data_ended && + jpeg->input_state.pub.bytes_in_buffer < 200) + return 1; + + /* + * Cycle the state machine until done or forced to suspend by lack of input. + * Note that we are able to progress through multiple states per call + * as long as the input is there. + */ + + for ( ; ; ) + { + switch (jpeg->state) + { + case CH_JPEG_READ_HEADER: + rval = lf_read_header(jpeg); + break; + case CH_JPEG_START_DECOMPRESS: + rval = lf_start_decompress(jpeg); + break; + case CH_JPEG_START_OUTPUT: + rval = lf_start_output(jpeg); + break; + case CH_JPEG_READ_IMAGE: + rval = lf_read_image(jpeg); + break; + case CH_JPEG_FINISH_OUTPUT: + rval = lf_finish_output(jpeg); + break; + case CH_JPEG_FINISH_DECOMPRESS: + rval = lf_finish_decompress(jpeg); + break; + case CH_JPEG_FINISHED: + rval = CH_JPEG_FINISHED; + break; + default: + fprintf(stderr, "jpegAddData: bogus state %d\n", jpeg->state); + rval = CH_JPEG_ERROR; + break; + } + /* exit loop for FINISHED, NEED_DATA, or ERROR returns */ + if (rval != CH_JPEG_SUCCESS) + break; + } + + /* + * Record how much data libjpeg consumed. + */ + if (jpeg->input_state.faked_eoi) + { + /* next_input_bytes is invalid, just say we ate it all. */ + jpeg->input_state.bytes_consumed = len; + } + else + { + jpeg->input_state.bytes_consumed = + jpeg->input_state.pub.next_input_byte - ((JOCTET*) data); + } + + if (rval == CH_JPEG_NEED_DATA) + return 1; + if (rval == CH_JPEG_FINISHED) + return 0; + return(-1); +} + +/* + * jpegInit + * + * Initialize JPEG reader state + */ +void +jpegInit(lineProc, lineClosure, if_vector) +void (*lineProc)(); +void *lineClosure; +struct ifs_vector *if_vector; +{ + jpegState *jpeg; + + /* + * Initialise vector for methods Chimera needs + */ + + if_vector->initProc = &jpegInit; + if_vector->destroyProc = &jpegDestroy; + if_vector->addDataProc = &jpegAddData; + if_vector->getImageProc = &jpegGetImage; + + /* allocate my workspace */ + jpeg = (jpegState *)alloc_mem(sizeof(jpegState)); + if_vector->image_format_closure = (void *)jpeg; + if (jpeg == NULL) + return; + + memset(jpeg, 0, sizeof(jpegState)); + jpeg->state = CH_JPEG_READ_HEADER; + jpeg->lineProc = lineProc; + jpeg->lineClosure = lineClosure; + + /* + * Initialise error handler. + * We set up the normal JPEG error routines, then override error_exit. + */ + jpeg->cinfo.err = jpeg_std_error(&jpeg->error_state.pub); + jpeg->error_state.pub.error_exit = &lf_error_exit_callback; + if (setjmp(jpeg->error_state.jump_buffer)) + { + /* + * We get here after a failure in initialisation. Lets just assume that + * people are careful about the order in which things are done, and that + * a destroy will work here. + */ + jpegDestroy((void *)jpeg); + /* + * It would be nice if init could indicate failure... + */ + return; + } + + /* + * Initialise jpeg library + */ + jpeg->destroy_jpeg = true; /* jpeg_destroy is OK even if create fails */ + jpeg_create_decompress(&jpeg->cinfo); + + /* + * Initialise our input reader. + */ + jpeg->cinfo.src = &jpeg->input_state.pub; + jpeg->input_state.pub.init_source = &lf_init_source_callback; + jpeg->input_state.pub.fill_input_buffer = &lf_fill_input_buffer_callback; + jpeg->input_state.pub.skip_input_data = &lf_skip_input_data_callback; + jpeg->input_state.pub.resync_to_restart = &jpeg_resync_to_restart; + jpeg->input_state.pub.term_source = &lf_term_source_callback; + jpeg->input_state.pub.bytes_in_buffer = 0; + jpeg->input_state.pub.next_input_byte = NULL; + + jpeg->input_state.bytes_consumed = 0; + jpeg->input_state.at_eof = false; +} diff --git a/image/jpegp.h b/image/jpegp.h new file mode 100644 index 0000000..2f94f15 --- /dev/null +++ b/image/jpegp.h @@ -0,0 +1,114 @@ +/* + * jpegp.h + * + * Copyright (C) 1995, Erik Corry (ehcorry@inet.uni-c.dk) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* + * This stuff is very fragile wrt. changes in the sizes of the + * jpeg library structures. It's a real good idea to be using libjpeg + * version 6a or later; 6a added some defenses against structure mismatches. + */ + +#ifndef JPEG_H_INCLUDED +#define JPEG_H_INCLUDED 1 + +#include +#include /* in case size_t is not defined by stdio.h */ +#include + +#include "image_format.h" + +/* + * Unfortunately libjpeg uses some of the same configuration symbols + * as Chimera. Undef'ing these symbols here is not essential, but it + * prevents macro-redefinition warnings from gcc and like-minded compilers. + */ +#undef HAVE_PROTOTYPES +#undef HAVE_UNSIGNED_CHAR +#undef HAVE_UNSIGNED_SHORT +#undef HAVE_STDDEF_H +#undef HAVE_STDLIB_H + +#include "jpeglib.h" +#include "jerror.h" + +/* + * Each of these structures begins with a jpeg library struct, + * followed by chimera extension fields. This lets us cast pointers + * back and forth between the structs known to the library and the + * full struct definitions. Poor man's subclassing, if you like. + */ + +struct jpeg_chimera_error_state +{ + struct jpeg_error_mgr pub; /* - this struct comes from jpeg lib */ + jmp_buf jump_buffer; /* usually an array of sorts from setjmp.h */ +}; + +struct jpeg_chimera_input_state +{ + struct jpeg_source_mgr pub; /* - this struct comes from jpeg lib */ + int bytes_consumed; /* # bytes already consumed by jpeg lib */ + bool at_eof; /* indicates we've gotten the whole file */ + bool faked_eoi; /* indicates next_input_byte is phony */ + JOCTET fake_eoi[2]; /* workspace for making a fake EOI */ +}; + +/* + * jpegState + */ +typedef struct jpeg_state +{ + struct jpeg_decompress_struct cinfo; /* - this struct comes from jpeg lib */ + + struct jpeg_chimera_error_state error_state; /* subsidiary structures */ + struct jpeg_chimera_input_state input_state; + + bool destroy_jpeg; /* indicates we need to call jpeg_destroy */ + int state; /* state of JPEG reader */ + + JDIMENSION ypos; /* current scanline number */ + + FormatLineProc lineProc; /* line callback */ + void *lineClosure; /* closure for callback */ + + Image *image; + + Intensity cmap[256]; /* room for making a grayscale palette */ +} jpegState; + +/* + * state of JPEG reader + */ +#define CH_JPEG_READ_HEADER 1 +#define CH_JPEG_START_DECOMPRESS 2 +#define CH_JPEG_START_OUTPUT 3 +#define CH_JPEG_READ_IMAGE 4 +#define CH_JPEG_FINISH_OUTPUT 5 +#define CH_JPEG_FINISH_DECOMPRESS 6 +#define CH_JPEG_FINISHED 0 + +/* + * return values from jpeg processing fns + * NOTE: CH_JPEG_FINISHED is also used as a return value! + */ +#define CH_JPEG_SUCCESS 1 +#define CH_JPEG_NEED_DATA 2 +#define CH_JPEG_ERROR 3 + +#endif diff --git a/image/new.c b/image/new.c new file mode 100644 index 0000000..2e9318e --- /dev/null +++ b/image/new.c @@ -0,0 +1,176 @@ +/* new.c: + * + * functions to allocate and deallocate structures and structure data + * + * jim frost 09.29.89 + * + * Copyright 1989, 1991 Jim Frost. + * + * Permission to use, copy, modify, distribute, and sell this software + * and its documentation for any purpose is hereby granted without fee, + * provided that the above copyright notice appear in all copies and + * that both that copyright notice and this permission notice appear + * in supporting documentation. The author makes no representations + * about the suitability of this software for any purpose. It is + * provided "as is" without express or implied warranty. + * + * THE AUTHOR DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, + * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN + * NO EVENT SHALL THE AUTHOR BE LIABLE FOR ANY SPECIAL, INDIRECT OR + * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS + * OF USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE + * OR OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE + * USE OR PERFORMANCE OF THIS SOFTWARE. + */ + +#include "port_before.h" + +#include +#include + +#ifdef HAVE_STDLIB_H +#include +#endif + +#include "port_after.h" + +#include "common.h" + +#include "imagep.h" + +Image * +newBitImage(width, height) +unsigned int width, height; +{ + unsigned long datasize; + Image *image; + + image = (Image *)alloc_mem(sizeof(Image)); + if (!image) return((Image *) 0); + + memset(image, 0, sizeof(Image)); + image->type = IBITMAP; + image->width = width; + image->height = height; + image->depth = 1; + + image->pixlen = 1; + /* round bytes_per_line up to nearest byte */ + image->bytes_per_line = ((width - 1) / CHAR_BITS) + 1; + /* round bytes_per_line up to nearest longword */ + image->bytes_per_line = ((image->bytes_per_line - 1) / sizeof(long)) + 1; + image->bytes_per_line *= sizeof(long); + /* Allocate a little too much memory, to allow overread */ + datasize = image->bytes_per_line * height + 32; + image->data = (byte *)alloc_mem(datasize); + memset(image->data, 0, datasize); + image->transparent = -1; + + return(image); +} + +Image * +newRGBImage(width, height, depth) +unsigned int width, height, depth; +{ + Image *image; + unsigned int pixlen; + unsigned long datasize; + + if(depth == 1) + { + image = newBitImage(width, height); + image->type = IRGB; + return image; + } + + pixlen = ((depth - 1) / CHAR_BITS) + 1; + pixlen *= CHAR_BITS; + + if (pixlen == 0) pixlen = 1; + + image = (Image *)alloc_mem(sizeof(Image)); + if (!image) return((Image *)0); + + memset(image, 0, sizeof(Image)); + image->type = IRGB; + image->width = width; + image->height = height; + image->depth = depth; + image->pixlen = pixlen; + /* set bytes_per_line (pixlen is a multiple of CHAR_BITS) */ + image->bytes_per_line = (width * pixlen) / CHAR_BITS; + /* round bytes_per_line up to nearest longword */ + image->bytes_per_line = ((image->bytes_per_line - 1) / sizeof(long)) + 1; + image->bytes_per_line *= sizeof(long); + /* Allocate a little too much memory, to allow overread */ + datasize = image->bytes_per_line * height + 32; + image->data = (byte *)alloc_mem(datasize); + if (!image->data) + { + free_mem((char *)image); + return((Image *)0); + } + + memset(image->data, 0, datasize); + + image->rgb.used = 0; + image->rgb.compressed = 0; + image->rgb.size = 2; + + image->transparent = -1; + + return(image); +} + +Image * +newTrueImage(width, height) +unsigned int width, height; +{ + Image *image; + unsigned long datasize; + + image = (Image *)alloc_mem(sizeof(Image)); + if (!image) return((Image *) 0); + + memset(image, 0, sizeof(Image)); + image->type = ITRUE; + image->width = width; + image->height = height; + image->depth = 8; + image->pixlen = 24; + /* set bytes_per_line */ + image->bytes_per_line = width * 3; + /* round bytes_per_line up to nearest longword */ + image->bytes_per_line = ((image->bytes_per_line - 1) / sizeof(long)) + 1; + image->bytes_per_line *= sizeof(long); + /* Allocate a little too much memory, to allow overread */ + datasize = image->bytes_per_line * height + 32; + image->data = (byte *)alloc_mem(datasize); + if (!image->data) + { + free_mem((char *)image); + return ((Image *)0); + } + + memset(image->data, 0, datasize); + + image->rgb.used = 0; + image->rgb.compressed = 0; + image->rgb.size = 2; + + image->transparent = -1; + + return(image); +} + +void +freeImage(image) +Image *image; +{ + if (image->data != NULL) free_mem((char *)image->data); + image->data = NULL; + free_mem((char *)image); + + return; +} diff --git a/image/png.c b/image/png.c new file mode 100644 index 0000000..2c77a0b --- /dev/null +++ b/image/png.c @@ -0,0 +1,310 @@ +/* + * Portable Network Graphics (PNG) image format + */ + +#include "port_before.h" + +#include + +#include "port_after.h" + +#include "common.h" +#include "imagep.h" +#include "image_format.h" +#include "image_endian.h" +#include + +#define PM_SCALE(a, b, c) (long)((a) * (c))/(b) + +enum { + image_success = 0, image_need_data = 1, image_error = -1 +}; + +typedef struct { + png_struct *state; + png_info *info; + Image *image; + Intensity cmap[3][256]; + void (*lineProc)(void *, int, int); + void *closure; + int lenSoFar; + int done; +} pngState; + + +static Image * +pngGetImage(void *pointer) +{ + return ((pngState *) pointer)->image; +} + + +static void lc_reverse_byte(byte *row, int n) +{ + int i; + byte c; + + for (i = 0; i < n; ++i) { + c = row[i]; + c = ((c << 4) & 0xf0) | ((c >> 4) & 0x0f); + c = ((c << 2) & 0xcc) | ((c >> 2) & 0x33); + c = ((c << 1) & 0xaa) | ((c >> 1) & 0x55); + row[i] = c; + } +} + + +static void +lf_info_callback(png_struct *state, png_info *info) +{ + int orig_depth = 0; + pngState *png = (pngState *) png_get_progressive_ptr(state); + + if (info->bit_depth < 8 && (PNG_COLOR_TYPE_RGB == info->color_type || + PNG_COLOR_TYPE_RGB_ALPHA == info->color_type)) + png_set_expand(state); + + /* I wish the frame's background colour was available here */ + if (info->color_type & PNG_COLOR_MASK_ALPHA) { + png_color_16 bg; + int gflag = PNG_BACKGROUND_GAMMA_SCREEN; + double gval = 1.0; + int expand = 0; + + bg.red = bg.green = bg.blue = bg.gray = 0; + if (PNG_COLOR_TYPE_PALETTE == info->color_type) + png_set_expand(state); + + png_set_background(state, &bg, gflag, expand, gval); + } + + if (info->bit_depth < 8 && (info->bit_depth > 1 || + PNG_COLOR_TYPE_GRAY != info->color_type)) { + if (PNG_COLOR_TYPE_GRAY == info->color_type) + orig_depth = info->bit_depth; + png_set_packing(state); + } + + /* tell libpng to strip 16 bit depth files down to 8 bits */ + if (info->bit_depth > 8) + png_set_strip_16(state); + + png_set_interlace_handling(state); + + /* update palette with transformations, update the info structure */ + png_read_update_info(state, info); + + /* allocate the memory to hold the image using the fields of png_info. */ + if (PNG_COLOR_TYPE_GRAY == info->color_type && 1 == info->bit_depth) { + png->image = newBitImage(info->width, info->height); + if (!png->image) { + png->done = image_error; + return; + } + + png_set_invert_mono(state); + } else if (PNG_COLOR_TYPE_PALETTE == info->color_type) { + int i; + + png->image = newRGBImage(info->width, info->height, info->bit_depth); + if (!png->image) { + png->done = image_error; + return; + } + + png->image->rgb.red = png->cmap[0]; + png->image->rgb.green = png->cmap[1]; + png->image->rgb.blue = png->cmap[2]; + for (i = 0; i < info->num_palette; ++i) { + png->image->rgb.red[i] = info->palette[i].red << 8; + png->image->rgb.green[i] = info->palette[i].green << 8; + png->image->rgb.blue[i] = info->palette[i].blue << 8; + } + png->image->rgb.used = info->num_palette; + if (info->valid & PNG_INFO_tRNS) { + int val, i; + + val = 0; + for (i = 0; i < info->num_trans; ++i) { + if (info->trans[i] < info->trans[val]) + val = i; + } + png->image->transparent = val; + } + } else if (PNG_COLOR_TYPE_GRAY == info->color_type) { + int i; + int depth = orig_depth ? orig_depth : info->bit_depth; + int maxval = (1 << depth) - 1; + + png->image = newRGBImage(info->width, info->height, depth); + if (!png->image) { + png->done = image_error; + return; + } + + /* png->image->type = IGRAY; */ + png->image->rgb.red = png->cmap[0]; + png->image->rgb.green = png->cmap[1]; + png->image->rgb.blue = png->cmap[2]; + for (i = 0; i <= maxval; i++) { + png->image->rgb.red[i] = PM_SCALE(i, maxval, 0xffff); + png->image->rgb.green[i] = PM_SCALE(i, maxval, 0xffff); + png->image->rgb.blue[i] = PM_SCALE(i, maxval, 0xffff); + } + png->image->rgb.used = maxval + 1; + + if (info->valid & PNG_INFO_tRNS) + png->image->transparent = info->trans_values.gray; + } else { + png->image = newTrueImage(info->width, info->height); + if (!png->image) { + png->done = image_error; + return; + } + + } + + if (info->valid & PNG_INFO_gAMA && png->image->type != IBITMAP) + png->image->gamma = 1.0 / info->gamma; + + assert((png->image->width * png->image->pixlen + 7) / 8 == info->rowbytes); +} + + +static void +lf_row_callback(png_struct *state, png_byte *new_row, png_uint_32 row_num, + int pass) +{ + pngState *png; + byte *old_row; + + if (!new_row) + return; + + png = (pngState *) png_get_progressive_ptr(state); + if (!png->image) + return; + + old_row = png->image->data + png->image->bytes_per_line * row_num; + + png_progressive_combine_row(state, old_row, new_row); + if (png->lineProc) { + /* I can't say I'm too fond of this endian business. */ +#ifdef CHIMERA_LITTLE_ENDIAN + if (IBITMAP == png->image->type) + lc_reverse_byte(old_row, png->info->rowbytes); +#endif + (png->lineProc)(png->closure, row_num, row_num); +#ifdef CHIMERA_LITTLE_ENDIAN + if (IBITMAP == png->image->type) + lc_reverse_byte(old_row,png->info->rowbytes); +#endif + } +} + + +static void +lf_end_callback(png_struct *state, png_info *info) +{ + pngState *png= (pngState *) png_get_progressive_ptr(state); + png->done = image_success; +} + + +static void +pngDestroy(void *pointer) +{ + pngState *png = (pngState *) pointer; + + if (!png) + return; + + if (setjmp(png->state->jmpbuf)) + return; + + if (png->state) { + png_read_destroy(png->state, png->info, (png_info *) 0); + free_mem(png->state); + png->state = 0; + } + + if (png->info) { + free_mem(png->info); + png->info = 0; + } + + if (png->image) { + freeImage(png->image); + png->image = 0; + } + + free_mem(png); +} + +static int +pngAddData(void *pointer, byte *data, int len, bool data_ended) +{ + pngState *png = (pngState *) pointer; + + if (setjmp(png->state->jmpbuf)) + return image_error; + + if (len > png->lenSoFar) { + png_process_data(png->state, png->info, data + png->lenSoFar, + len - png->lenSoFar); + png->lenSoFar = len; + } + + if (image_need_data == png->done && data_ended) + return image_error; + + return png->done; +} + + +void +pngInit(void (*lineProc)(void *, int, int), void *closure, struct ifs_vector *ifsv) +{ + pngState *png; + + ifsv->image_format_closure = 0; + png = (pngState *) alloc_mem(sizeof(pngState)); + if (!png) + return; + + memset(png, 0, sizeof(pngState)); + png->lineProc = lineProc; + png->closure = closure; + png->state = (png_struct *) alloc_mem(sizeof(png_struct)); + if (!png->state) + return; + + png->info = (png_info *) alloc_mem(sizeof(png_info)); + if (!png->info) { + free_mem(png->state); + return; + } + + if (setjmp(png->state->jmpbuf)) { + png_read_destroy(png->state, png->info, (png_info *) 0); + free_mem(png->state); + free_mem(png->info); + png->state = 0; + png->info = 0; + return; + } + + png_info_init(png->info); + png_read_init(png->state); + + png_set_progressive_read_fn(png->state, (void *) png, lf_info_callback, + lf_row_callback, lf_end_callback); + png->done = image_need_data; + png->lenSoFar = 0; + + ifsv->initProc = &pngInit; + ifsv->destroyProc = &pngDestroy; + ifsv->addDataProc = &pngAddData; + ifsv->getImageProc = &pngGetImage; + ifsv->image_format_closure = (void *) png; +} diff --git a/image/pnm.c b/image/pnm.c new file mode 100644 index 0000000..105b07b --- /dev/null +++ b/image/pnm.c @@ -0,0 +1,568 @@ +/* + * pnm.c: + * + * (c) 1995 Erik Corry ehcorry@inet.uni-c.dk + * + * loosely modelled on gif.c. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "port_before.h" + +#include +#include + +#include "port_after.h" + +#include "common.h" + +#include "image_endian.h" +#include "imagep.h" +#include "pnmp.h" + +static byte lc_reverse_byte[] = +{ +0, 128, 64, 192, 32, 160, 96, 224, 16, 144, 80, 208, 48, 176, 112, 240, +8, 136, 72, 200, 40, 168, 104, 232, 24, 152, 88, 216, 56, 184, 120, 248, +4, 132, 68, 196, 36, 164, 100, 228, 20, 148, 84, 212, 52, 180, 116, 244, +12, 140, 76, 204, 44, 172, 108, 236, 28, 156, 92, 220, 60, 188, 124, 252, +2, 130, 66, 194, 34, 162, 98, 226, 18, 146, 82, 210, 50, 178, 114, 242, +10, 138, 74, 202, 42, 170, 106, 234, 26, 154, 90, 218, 58, 186, 122, 250, +6, 134, 70, 198, 38, 166, 102, 230, 22, 150, 86, 214, 54, 182, 118, 246, +14, 142, 78, 206, 46, 174, 110, 238, 30, 158, 94, 222, 62, 190, 126, 254, +1, 129, 65, 193, 33, 161, 97, 225, 17, 145, 81, 209, 49, 177, 113, 241, +9, 137, 73, 201, 41, 169, 105, 233, 25, 153, 89, 217, 57, 185, 121, 249, +5, 133, 69, 197, 37, 165, 101, 229, 21, 149, 85, 213, 53, 181, 117, 245, +13, 141, 77, 205, 45, 173, 109, 237, 29, 157, 93, 221, 61, 189, 125, 253, +3, 131, 67, 195, 35, 163, 99, 227, 19, 147, 83, 211, 51, 179, 115, 243, +11, 139, 75, 203, 43, 171, 107, 235, 27, 155, 91, 219, 59, 187, 123, 251, +7, 135, 71, 199, 39, 167, 103, 231, 23, 151, 87, 215, 55, 183, 119, 247, +15, 143, 79, 207, 47, 175, 111, 239, 31, 159, 95, 223, 63, 191, 127, 255}; + +/* + * pnmDestroy + */ +static void +pnmDestroy(pointer) +void *pointer; +{ + pnmState *pnm = (pnmState *)pointer; + if (pnm->free_input_table && pnm->input_table) + free(pnm->input_table); + pnm->input_table = 0; + pnm->free_input_table = false; + if (pnm->image) + freeImage(pnm->image); + pnm->image = 0; + if (pnm != NULL) free_mem(pnm); + + return; +} + +static int +lf_read_magic( + pnmState *pnm, + byte *data, + int len) +{ + if(len < 2) return PNM_NEED_DATA; + if(data[0] != 'P') return PNM_ERROR; + if(data[1] < '1' || data[1] > '6') return PNM_ERROR; + pnm->pnm_class = data[1] - '0'; + pnm->pos = 2; + pnm->state = PNM_READ_WIDTH; + return PNM_SUCCESS; +} + +static void +lf_skip_line( + pnmState *pnm, + byte *data, + int len) +{ + int pos = pnm->pos; + while(pos < len && data[pos] != '\n' && data[pos] != '\r') + pos++; + + /* + * end of line not found? + */ + if(pos >= len) return; + + /* + * Check for CR-LF + */ + if(pos < len-1 && data[pos] == '\r' && data[pos+1] == '\n') + { + pos += 2; + pnm->pos = pos; + return; + } + + /* + * Check for LF-CR (does this exist?) + */ + if(pos < len-1 && data[pos] == '\n' && data[pos+1] == '\r') + { + pos += 2; + pnm->mac_newlines = true; + pnm->pos = pos; + return; + } + + /* + * Check for LF-endofdata or CR-endofdata + */ + if(pos >= len-1 && (data[pos] == '\n' || data[pos] == '\r')) + { + return; + } + + /* + * Check for CR alone + */ + if(data[pos] == '\r') + pnm->mac_newlines = true; + + pnm->pos = pos + 1; + return; +} + +static int +lf_skip_whitespace( + pnmState *pnm, + byte *data, + int len) +{ + while(pnm->pos < len) + { + while(data[pnm->pos] == ' ' || + data[pnm->pos] == '\n' || + data[pnm->pos] == '\r' || + data[pnm->pos] == '\t') + { + if(pnm->pos && data[pnm->pos] == '\r' && data[pnm->pos-1] != '\n') + pnm->mac_newlines = true; + pnm->pos++; + if(pnm->pos >= len) return PNM_NEED_DATA; + } + if(data[pnm->pos] == '#') + { + int t = pnm->pos; + lf_skip_line(pnm, data, len); + if(t == pnm->pos) return PNM_NEED_DATA; + } + else + { + break; + } + } + return PNM_SUCCESS; +} + +static bool +lf_punctuation_ahead( + pnmState *pnm, + byte *data, + int len) +{ + byte *i; + for(i = data + pnm->pos; i < data + len; i++) + if((*i < '0' || *i > '9') && + (*i < 'a' || *i > 'z') && + (*i < 'A' || *i > 'Z')) + return true; + return false; +} + +static bool +lf_read_int( + pnmState *pnm, + byte *data, + int len, + int *returnval) +{ + byte *new_pos; + long answer = strtol((char *)(data + pnm->pos), (char **)(&new_pos), 10); + if(new_pos == data + pnm->pos) return true; + pnm->pos = new_pos - data; + *returnval = answer; + return false; +} + +static int +lf_read_width( + pnmState *pnm, + byte *data, + int len) +{ + int a; + if(lf_skip_whitespace(pnm, data, len) == PNM_NEED_DATA) return PNM_NEED_DATA; + if(pnm->pos >= len) return PNM_NEED_DATA; + if(!lf_punctuation_ahead(pnm, data, len)) return PNM_NEED_DATA; + if(lf_read_int(pnm, data, len, &a)) return PNM_ERROR; + pnm->width = a; + pnm->state = PNM_READ_HEIGHT; + return PNM_SUCCESS; +} + +static int +lf_read_height( + pnmState *pnm, + byte *data, + int len) +{ + int height; + int i; + if(lf_skip_whitespace(pnm, data, len) == PNM_NEED_DATA) return PNM_NEED_DATA; + if(pnm->pos >= len) return PNM_NEED_DATA; + if(!lf_punctuation_ahead(pnm, data, len)) return PNM_NEED_DATA; + if(lf_read_int(pnm, data, len, &height)) return PNM_ERROR; + switch(pnm->pnm_class) + { + case 1: + case 4: + pnm->image = newBitImage(pnm->width, height); + if (!pnm->image) return PNM_ERROR; + pnm->imagepos = pnm->image->data; + if(pnm->pnm_class == 1) pnm->state = PNM_READ_ASC_BIT; + else pnm->state = PNM_READ_PRE_RAW_NEWLINE; +# ifdef CHIMERA_LITTLE_ENDIAN + if(pnm->pnm_class == 1) + pnm->input_table = 0; /* ascii bytes will be assembled l-e */ + else + pnm->input_table = lc_reverse_byte; /* raw bytes are big-endian */ +# else + if(pnm->pnm_class == 1) + pnm->input_table = lc_reverse_byte; /* ascii bytes will be assembled l-e */ + else + pnm->input_table = 0; /* raw bytes are big-endian */ +# endif + break; + case 2: + case 5: + pnm->image = newRGBImage(pnm->width, height, 8); + if (!pnm->image) return PNM_ERROR; + pnm->imagepos = pnm->image->data; + pnm->image->type = IGRAY; + pnm->image->rgb.red = pnm->cmap[0]; + pnm->image->rgb.green = pnm->cmap[1]; + pnm->image->rgb.blue = pnm->cmap[2]; + for(i = 0; i < 256; i++) + { + pnm->image->rgb.red[i] = i | (i << 8); + pnm->image->rgb.green[i] = i | (i << 8); + pnm->image->rgb.blue[i] = i | (i << 8); + } + pnm->state = PNM_READ_MAX_VAL; + break; + case 3: + case 6: + pnm->image = newTrueImage(pnm->width, height); + if (!pnm->image) return PNM_ERROR; + pnm->imagepos = pnm->image->data; + pnm->state = PNM_READ_MAX_VAL; + break; + } + return PNM_SUCCESS; +} + +static int +lf_read_max_val( + pnmState *pnm, + byte *data, + int len) +{ + int i; + int max_val; + if(lf_skip_whitespace(pnm, data, len) == PNM_NEED_DATA) return PNM_NEED_DATA; + if(pnm->pos >= len) return PNM_NEED_DATA; + if(!lf_punctuation_ahead(pnm, data, len)) return PNM_NEED_DATA; + if(lf_read_int(pnm, data, len, &max_val)) return PNM_ERROR; + + if(max_val > 65535) return PNM_ERROR; + if(max_val < 1) return PNM_ERROR; + if(pnm->pnm_class > 3 && max_val > 255) return PNM_ERROR; + + pnm->max_val = max_val; + if(pnm->pnm_class > 3) + pnm->state = PNM_READ_PRE_RAW_NEWLINE; + else + pnm->state = PNM_READ_ASC; + + if(max_val == 255) return PNM_SUCCESS; + + if(pnm->pnm_class > 3) + pnm->input_table = (byte *)calloc_mem(1, 256); + else + pnm->input_table = (byte *)calloc_mem(1, max_val); + + pnm->free_input_table = true; + + for(i = 0; i < max_val; i++) + pnm->input_table[i] = 255.0 * (((double)i) / ((double)max_val) + 0.0001); + + return PNM_SUCCESS; +} + +static int +lf_read_pre_raw_newline( + pnmState *pnm, + byte *data, + int len) +{ + while(pnm->pos < len && data[pnm->pos] != '\n' && data[pnm->pos] != '\r') + pnm->pos++; + if(pnm->pos >= len) return PNM_NEED_DATA; + if(pnm->mac_newlines && data[pnm->pos] == '\n') + { + if(pnm->pos >= len-1) + return PNM_NEED_DATA; + else + pnm->pos++; + } + pnm->pos++; + pnm->state = PNM_READ_RAW; + return PNM_SUCCESS; +} + +static int +lf_read_raw( + pnmState *pnm, + byte *data, + int len) +{ + int bytes_per_line = pnm->image->width * pnm->image->pixlen; + bytes_per_line += CHAR_BITS - 1; + bytes_per_line /= CHAR_BITS; + while(pnm->ypos < pnm->image->height && len - pnm->pos >= bytes_per_line) + { + int i; + byte *imagepos = pnm->imagepos; + byte *sourcepos = data + pnm->pos; + byte *table = pnm->input_table; + + if(table) for(i = bytes_per_line; i; i--) + { + *imagepos++ = table[*sourcepos++]; + } + else + { + memcpy(imagepos, sourcepos, bytes_per_line); + imagepos += bytes_per_line; + sourcepos += bytes_per_line; + } + + pnm->imagepos += pnm->image->bytes_per_line; + pnm->pos += bytes_per_line; + if(pnm->lineProc) pnm->lineProc(pnm->closure, pnm->ypos, pnm->ypos); + pnm->ypos++; + } + if(pnm->ypos < pnm->image->height) return PNM_NEED_DATA; + pnm->state = PNM_FINISHED; + return PNM_SUCCESS; +} + +static int +lf_read_asc( + pnmState *pnm, + byte *data, + int len) +{ + int bytes_per_line = pnm->image->width * pnm->image->pixlen; + bytes_per_line += CHAR_BITS - 1; + bytes_per_line /= CHAR_BITS; + + while(pnm->ypos < pnm->image->height) + { + if(lf_skip_whitespace(pnm, data, len) == PNM_NEED_DATA) + return PNM_NEED_DATA; + if(data[pnm->pos] == '#') + { + int t = pnm->pos; + lf_skip_line(pnm, data, len); + if(t == pnm->pos) return PNM_NEED_DATA; + continue; + } + if(!lf_punctuation_ahead(pnm, data, len)) + return PNM_NEED_DATA; + { + int t; + if(lf_read_int(pnm, data, len, &t)) return PNM_ERROR; + if(t < 0) return PNM_ERROR; + if(t > pnm->max_val) return PNM_ERROR; + if(pnm->input_table) *pnm->imagepos++ = pnm->input_table[t]; + else *pnm->imagepos++ = t; + pnm->xpos++; + } + if(pnm->xpos >= bytes_per_line) + { + pnm->xpos = 0; + pnm->imagepos += pnm->image->bytes_per_line - bytes_per_line; + if(pnm->lineProc) pnm->lineProc(pnm->closure, pnm->ypos, pnm->ypos); + pnm->ypos++; + } + } + pnm->state = PNM_FINISHED; + return PNM_SUCCESS; +} + +/* + * This reads those 11010101 10 01101 10 10 101010-type pbm ascii bit + * files. I really don't like this format, so I'm not going to put a + * lot of effort into optimising this. It's an unashamed performance + * disaster. + */ + +static int +lf_read_asc_bit( + pnmState *pnm, + byte *data, + int len) +{ + int bytes_per_line = pnm->image->width; + bytes_per_line += CHAR_BITS - 1; + bytes_per_line /= CHAR_BITS; + + while(pnm->ypos < pnm->image->height) + { + while(pnm->pos < len && data[pnm->pos] != '1' && data[pnm->pos] != '0') + if(lf_skip_whitespace(pnm, data, len) == PNM_NEED_DATA) + return PNM_NEED_DATA; + if(pnm->pos >= len) return PNM_NEED_DATA; + + if(data[pnm->pos] == '1') + { +# ifdef CHIMERA_LITTLE_ENDIAN + *pnm->imagepos |= 1 << (pnm->xpos % CHAR_BITS); +# else + *pnm->imagepos |= 1 << (7 - (pnm->xpos % CHAR_BITS)); +# endif + } + pnm->pos++; + pnm->xpos++; + if(pnm->xpos % CHAR_BITS == 0) + { + pnm->imagepos++; + } + if(pnm->xpos >= pnm->image->width) + { + pnm->imagepos += pnm->image->bytes_per_line - bytes_per_line; + pnm->xpos = 0; + if(pnm->lineProc) pnm->lineProc(pnm->closure, pnm->ypos, pnm->ypos); + pnm->ypos++; + } + } + pnm->state = PNM_FINISHED; + return PNM_SUCCESS; +} + +/* + * pnmAddData + * + * 0 success + * 1 needs more data + * -1 error + * + * Assumes data is the address of the beginning of the pnm data and len + * is the total length. + */ +static int +pnmAddData(pointer, data, len, data_ended) +void *pointer; +byte *data; +int len; +bool data_ended; +{ + pnmState *pnm = (pnmState *)pointer; + int rval; + + for ( ; ; ) + { + switch (pnm->state) + { + case PNM_READ_MAGIC: + rval = lf_read_magic(pnm, data, len); + break; + case PNM_READ_WIDTH: + rval = lf_read_width(pnm, data, len); + break; + case PNM_READ_HEIGHT: + rval = lf_read_height(pnm, data, len); + break; + case PNM_READ_MAX_VAL: + rval = lf_read_max_val(pnm, data, len); + break; + case PNM_READ_PRE_RAW_NEWLINE: + rval = lf_read_pre_raw_newline(pnm, data, len); + break; + case PNM_READ_RAW: + rval = lf_read_raw(pnm, data, len); + break; + case PNM_READ_ASC: + rval = lf_read_asc(pnm, data, len); + break; + case PNM_READ_ASC_BIT: + rval = lf_read_asc_bit(pnm, data, len); + break; + case PNM_FINISHED: + return 0; + } + if (rval == PNM_NEED_DATA) + { + if(data_ended) return -1; + return(1); + } + else if (rval != PNM_SUCCESS) return(-1); + } + + return(-1); +} + +static Image * +pnmGetImage(void *pointer) +{ + pnmState *pnm = (pnmState *)pointer; + return pnm->image; +} + + +/* + * pnmInit + * + * Initialize PNM reader state + */ +void +pnmInit(lineProc, closure, if_vector) +FormatLineProc lineProc; +void *closure; +struct ifs_vector *if_vector; +{ + pnmState *pnm; + + pnm = (pnmState *)alloc_mem(sizeof(pnmState)); + memset(pnm, 0, sizeof(pnmState)); + pnm->state = PNM_READ_MAGIC; + pnm->lineProc = lineProc; + pnm->closure = closure; + + if_vector->image_format_closure = (void *)pnm; + if_vector->initProc = &pnmInit; + if_vector->destroyProc = &pnmDestroy; + if_vector->addDataProc = &pnmAddData; + if_vector->getImageProc = &pnmGetImage; +} + diff --git a/image/pnmp.h b/image/pnmp.h new file mode 100644 index 0000000..9f5af62 --- /dev/null +++ b/image/pnmp.h @@ -0,0 +1,72 @@ +/* + * pnm.h + * + * Copyright (C) 1995, Erik Corry (ehcorry@inet.uni-c.dk) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef PNM_H_INCLUDED +#define PNM_H_INCLUDED 1 + +#include "image_format.h" + +/* + * pnmState + */ +typedef struct pnm_state +{ + int state; /* state of PNM reader */ + FormatLineProc lineProc; /* line callback */ + void *closure; /* closure for callback */ + + Image *image; + + int pos; /* current read position */ + int ypos; /* current line */ + int xpos; /* current pixel */ + byte *imagepos; /* current posn. in output */ + bool mac_newlines; /* CR or LFCR for newlines */ + bool raw; /* raw file */ + byte *input_table; + bool free_input_table; + Intensity cmap[3][256]; /* colormap */ + int max_val; + int pnm_class; + int width; + +} pnmState; + +/* + * state of PNM reader + */ +#define PNM_READ_MAGIC 1 +#define PNM_READ_WIDTH 2 +#define PNM_READ_HEIGHT 3 +#define PNM_READ_MAX_VAL 4 +#define PNM_READ_PRE_RAW_NEWLINE 5 +#define PNM_READ_RAW 6 +#define PNM_READ_ASC 7 +#define PNM_READ_ASC_BIT 8 +#define PNM_FINISHED 0 + +/* + * return values from pnm processing fns + */ +#define PNM_ERROR 0 +#define PNM_SUCCESS 1 +#define PNM_NEED_DATA 2 + +#endif diff --git a/image/xbm.c b/image/xbm.c new file mode 100644 index 0000000..bde9ef1 --- /dev/null +++ b/image/xbm.c @@ -0,0 +1,379 @@ +/* + * xbm.c: + * + * (c) 1995 Erik Corry ehcorry@inet.uni-c.dk + * + * modelled on gif.c. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +#include "port_before.h" + +#include +#include + +#include "port_after.h" + +#include "common.h" + +#include "image_endian.h" +#include "imagep.h" +#include "xbmp.h" + +static byte lc_reverse_byte[] = +{ +0, 128, 64, 192, 32, 160, 96, 224, 16, 144, 80, 208, 48, 176, 112, 240, +8, 136, 72, 200, 40, 168, 104, 232, 24, 152, 88, 216, 56, 184, 120, 248, +4, 132, 68, 196, 36, 164, 100, 228, 20, 148, 84, 212, 52, 180, 116, 244, +12, 140, 76, 204, 44, 172, 108, 236, 28, 156, 92, 220, 60, 188, 124, 252, +2, 130, 66, 194, 34, 162, 98, 226, 18, 146, 82, 210, 50, 178, 114, 242, +10, 138, 74, 202, 42, 170, 106, 234, 26, 154, 90, 218, 58, 186, 122, 250, +6, 134, 70, 198, 38, 166, 102, 230, 22, 150, 86, 214, 54, 182, 118, 246, +14, 142, 78, 206, 46, 174, 110, 238, 30, 158, 94, 222, 62, 190, 126, 254, +1, 129, 65, 193, 33, 161, 97, 225, 17, 145, 81, 209, 49, 177, 113, 241, +9, 137, 73, 201, 41, 169, 105, 233, 25, 153, 89, 217, 57, 185, 121, 249, +5, 133, 69, 197, 37, 165, 101, 229, 21, 149, 85, 213, 53, 181, 117, 245, +13, 141, 77, 205, 45, 173, 109, 237, 29, 157, 93, 221, 61, 189, 125, 253, +3, 131, 67, 195, 35, 163, 99, 227, 19, 147, 83, 211, 51, 179, 115, 243, +11, 139, 75, 203, 43, 171, 107, 235, 27, 155, 91, 219, 59, 187, 123, 251, +7, 135, 71, 199, 39, 167, 103, 231, 23, 151, 87, 215, 55, 183, 119, 247, +15, 143, 79, 207, 47, 175, 111, 239, 31, 159, 95, 223, 63, 191, 127, 255}; + +/* + * xbmGetImage + */ + +static Image * +xbmGetImage(void *pointer) +{ + xbmState *xbm = (xbmState *)pointer; + return xbm->image; +} + +/* + * xbmDestroy + */ +static void +xbmDestroy(pointer) +void *pointer; +{ + xbmState *xbm = (xbmState *)pointer; + if (xbm->image) + freeImage(xbm->image); + xbm->image = 0; + if (xbm != NULL) free_mem(xbm); + + return; +} + +static bool +lf_read_int( + xbmState *xbm, + byte *data, + int len, + int *returnval) +{ + byte *new_pos; + long answer = strtol((char *)(data + xbm->pos), (char **)(&new_pos), 0); + if(new_pos == data + xbm->pos) return true; + xbm->pos = new_pos - data; + *returnval = answer; + return false; +} + +static bool +lf_find_xbm_token( + xbmState *xbm, + byte *data, + int len, + byte **token_start_return, + int *token_len_return) +{ + int oldpos=xbm->pos; + int i; + while (xbm->pos < len && data[xbm->pos] != ' ' && data[xbm->pos] != '\t' && + data[xbm->pos] != '\n' && data[xbm->pos] != '\r') + xbm->pos++; + if(xbm->pos >= len) return true; + if(data[xbm->pos] == '\n' || data[xbm->pos] == '\r') return true; + for(i = xbm->pos - 2; i >= oldpos; i--) + if(data[i] == '_') break; + if(i < oldpos) return true; + *token_start_return = data + i + 1; + *token_len_return = xbm->pos - i - 1; + return false; +} + +static bool +lf_find_define( + xbmState *xbm, + byte *data, + int len) +{ + if(xbm->pos + 6 > len) return true; + if(strncmp("define", data + xbm->pos, 6) != 0) return true; + xbm->pos += 6; + return false; +} + +static bool +lf_skip_line( + xbmState *xbm, + byte *data, + int len) +{ + while (xbm->pos < len && data[xbm->pos] != '\n' && data[xbm->pos] == '\r') + xbm->pos++; + if(xbm->pos >= len) return true; + /* + * Check for CR-LF line ending. Is this necessary??? + */ + if(data[xbm->pos] == '\r' && xbm->pos + 1 < len && data[xbm->pos + 1] == '\n') + xbm->pos++; + xbm->pos++; + return false; +} + +static bool +lf_skip_space_and_tabs( + xbmState *xbm, + byte *data, + int len) +{ + while (xbm->pos < len && (data[xbm->pos] == ' ' || data[xbm->pos] == '\t')) + xbm->pos++; + if(xbm->pos >= len) return true; + return false; +} + +static int +lf_header_arrived( + xbmState *xbm, + byte *data, + int len) +{ + byte *token_start; + int token_len; + int width, height; + bool width_found = false; + bool height_found = false; + while(!width_found || !height_found) + { + while(xbm->pos < len && data[xbm->pos] != '#') + lf_skip_line(xbm, data, len); + xbm->pos++; /* step past # */ + if(xbm->pos >= len) return XBM_ERROR; + if(lf_skip_space_and_tabs(xbm, data, len)) return XBM_ERROR; + if(lf_find_define(xbm, data, len)) return XBM_ERROR; + if(lf_skip_space_and_tabs(xbm, data, len)) return XBM_ERROR; + if(lf_find_xbm_token(xbm, data, len, &token_start, &token_len)) return XBM_ERROR; + if(lf_skip_space_and_tabs(xbm, data, len)) return XBM_ERROR; + if(token_len == 5 && strncmp("width", token_start, token_len) == 0) + { + if(lf_read_int(xbm, data, len, &width)) return XBM_ERROR; + if(width < 1 || width > 65535) return XBM_ERROR; + width_found = true; + } + else if(token_len == 6 && strncmp("height", token_start, token_len) == 0) + { + if(lf_read_int(xbm, data, len, &height)) return XBM_ERROR; + if(height < 1 || height > 65535) return XBM_ERROR; + height_found = true; + } + lf_skip_line(xbm, data, len); + } + while(xbm->pos < len && data[xbm->pos] != '{') /* } */ + xbm->pos++; + if(xbm->pos >= len) return XBM_ERROR; + + /* + * Header successfully found. Now make image structure. + */ + + xbm->image = newBitImage(width, height); + if (!xbm->image) return XBM_ERROR; + xbm->imagepos = xbm->image->data; + + return XBM_SUCCESS; +} + +static int +lf_read_header( + xbmState *xbm, + byte *data, + int len) +{ + int i; + for (i = xbm->pos; i < len-1; i++) + { + if(data[i] == '{') + { + int status = lf_header_arrived(xbm, data, len); + xbm->state = XBM_READ_IMAGE; + xbm->pos = i+1; + return status; + } + } + return XBM_NEED_DATA; +} + +static void +lf_wrap_x(xbmState *xbm) +{ + if(xbm->xpos >= xbm->image->width) + { + if (xbm->lineProc != NULL) + { + (xbm->lineProc)(xbm->closure, xbm->ypos, xbm->ypos); + } + xbm->xpos = 0; + xbm->ypos++; + xbm->imagepos += xbm->image->bytes_per_line - + ((xbm->image->width + CHAR_BITS - 1) / CHAR_BITS); + } +} + +static bool +lf_skip_non_number( + xbmState *xbm, + byte *data, + int len) +{ + byte *i; + for(i = data + xbm->pos; i < data + len; i++) + if((*i >= '0' && *i <= '9') || + (*i >= 'a' && *i <= 'z') || + (*i >= 'A' && *i <= 'Z')) + { + xbm->pos = i - data; + return true; + } + return false; +} + +static bool +lf_punctuation_ahead( + xbmState *xbm, + byte *data, + int len) +{ + byte *i; + for(i = data + xbm->pos; i < data + len; i++) + if((*i < '0' || *i > '9') && + (*i < 'a' || *i > 'z') && + (*i < 'A' || *i > 'Z')) + return true; + return false; +} + +static int +lf_read_image( + xbmState *xbm, + byte *data, + int len) +{ + while(lf_skip_non_number(xbm, data, len) && + lf_punctuation_ahead(xbm, data, len)) + { + int t; + lf_wrap_x(xbm); + if(xbm->ypos >= xbm->image->height) + { xbm->state = XBM_FINISHED; return XBM_SUCCESS; } + if(lf_read_int(xbm, data, len, &t)) return XBM_ERROR; + t &= 255; +# ifdef CHIMERA_BIG_ENDIAN + t = lc_reverse_byte[t]; +# endif + *xbm->imagepos++ = t; + xbm->xpos += CHAR_BITS; + } + + lf_wrap_x(xbm); + if(xbm->ypos >= xbm->image->height) + { xbm->state = XBM_FINISHED; return XBM_SUCCESS; } + + return XBM_NEED_DATA; +} + +/* + * xbmAddData + * + * 0 success + * 1 needs more data + * -1 error + * + * Assumes data is the address of the beginning of the xbm data and len + * is the total length. + */ +static int +xbmAddData(pointer, data, len, data_ended) +void *pointer; +byte *data; +int len; +bool data_ended; +{ + xbmState *xbm = (xbmState *)pointer; + int rval; + + for ( ; ; ) + { + switch (xbm->state) + { + case XBM_READ_HEADER: + rval = lf_read_header(xbm, data, len); + break; + case XBM_READ_IMAGE: + rval = lf_read_image(xbm, data, len); + break; + case XBM_FINISHED: + return 0; + } + if (rval == XBM_NEED_DATA) + { + if (data_ended) return(-1); + return(1); + } + else if (rval != XBM_SUCCESS) return(-1); + } + + return(-1); +} + +/* + * xbmInit + * + * Initialize XBM reader state + */ +void +xbmInit(lineProc, closure, if_vector) +void (*lineProc)(); +void *closure; +struct ifs_vector *if_vector; +{ + xbmState *xbm; + + xbm = (xbmState *)alloc_mem(sizeof(xbmState)); + memset(xbm, 0, sizeof(xbmState)); + xbm->state = XBM_READ_HEADER; + xbm->lineProc = lineProc; + xbm->closure = closure; + + if_vector->initProc = &xbmInit; + if_vector->destroyProc = &xbmDestroy; + if_vector->addDataProc = &xbmAddData; + if_vector->getImageProc = &xbmGetImage; + + if_vector->image_format_closure = (void *)xbm; +} diff --git a/image/xbmp.h b/image/xbmp.h new file mode 100644 index 0000000..bd19261 --- /dev/null +++ b/image/xbmp.h @@ -0,0 +1,58 @@ +/* + * xbmp.h + * + * Copyright (C) 1995, Erik Corry (ehcorry@inet.uni-c.dk) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef XBM_H_INCLUDED +#define XBM_H_INCLUDED 1 + +#include "image_format.h" + +/* + * xbmState + */ +typedef struct xbm_state +{ + int state; /* state of XBM reader */ + FormatLineProc lineProc; /* line callback */ + void *closure; /* closure for callback */ + + Image *image; + + int pos; /* current read position */ + int ypos; /* current line */ + int xpos; /* current pixel */ + byte *imagepos; /* current posn. in output */ + +} xbmState; + +/* + * state of XBM reader + */ +#define XBM_FINISHED 0 +#define XBM_READ_HEADER 1 +#define XBM_READ_IMAGE 2 + +/* + * return values from xbm processing fns + */ +#define XBM_ERROR 0 +#define XBM_SUCCESS 1 +#define XBM_NEED_DATA 2 + +#endif diff --git a/image/xcolorcube.c b/image/xcolorcube.c new file mode 100644 index 0000000..f09bb03 --- /dev/null +++ b/image/xcolorcube.c @@ -0,0 +1,555 @@ +/* + * xcolorcube.c - Manage color allocation on X display + * + * (c) Copyright 1995 Erik Corry ehcorry@inet.uni-c.dk erik@kroete2.freinet.de + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#include "port_before.h" + +#include +#include +#include +#include + +#include "port_after.h" + +#include "colorcube.h" +#include "xcolorcube.h" + +typedef struct ls_cubealist +{ + Display *display; + Colormap colormap; + cct_cube cube; + struct ls_cubealist *next; +} *lt_cubealist; + +static lt_cubealist lv_cubealist = 0; +static lt_cubealist lv_grayalist = 0; + +static bool +lf_attempt_axbxc( + cct_cube cube, + Display *display, + Colormap colormap, + int a, + int b, + int c) +{ + unsigned long allocated[256]; + int la, lb, lc; + int n = 0; + + for(la = 0; la < a; la++) + { + cube->u.mapping.pixel_values[0][la] = b*c*la; + for(lb = 0; lb < b; lb++) + { + cube->u.mapping.pixel_values[1][lb] = c*lb; + for(lc = 0; lc < c; lc++) + { + XColor try; + int status; + cube->u.mapping.pixel_values[2][lc] = lc; + try.red = (unsigned short) (0.001+(65535.0 * la) / (double) (a - 1)); + try.green = (unsigned short) (0.001+(65535.0 * lb) / (double) (b - 1)); + try.blue = (unsigned short) (0.001+(65535.0 * lc) / (double) (c - 1)); + cube->u.mapping.brightnesses[0][la] = try.red >> 8; + cube->u.mapping.brightnesses[1][lb] = try.green >> 8; + cube->u.mapping.brightnesses[2][lc] = try.blue >> 8; + try.flags = DoRed | DoGreen | DoBlue; + status = XAllocColor(display, colormap, &try); + if(!status && n) + { + XFreeColors(display, colormap, allocated, n, 0); + return(false); + } + cube->u.mapping.mapping[n] = try.pixel; + allocated[n] = try.pixel; + n++; + } + } + } + + /* TODO: test whether mapping is really necessary */ + + cube->cube_type = cube_mapping; + cube->u.mapping.value_count[0] = a; + cube->u.mapping.value_count[1] = b; + cube->u.mapping.value_count[2] = c; + + if(getenv("CHIMERA_TEST_COLOR_ALLOCATION")) + printf("%dx%dx%d color cube allocated\n", a, b, c); + + return true; +} + + +cct_cube +xccf_allocate_cube( + Display *display, + Colormap colormap, + Visual *visual, + int depth) +{ + cct_cube cube; + lt_cubealist liststepper; + lt_cubealist *listextension; + + for(liststepper = lv_cubealist, listextension = &lv_cubealist; + liststepper; + listextension = &(liststepper->next), liststepper = liststepper->next) + { + if(liststepper->display == display && liststepper->colormap == colormap) + return liststepper->cube; + } + + *listextension = (lt_cubealist)calloc(1, sizeof(struct ls_cubealist)); + (*listextension)->display = display; + (*listextension)->colormap = colormap; + cube = (cct_cube)calloc(1, sizeof(struct ccs_cube)); + (*listextension)->cube = cube; + + if(visual->class == TrueColor) + { + cube->cube_type = cube_true_color; + cube->u.true_color.color_mask[0] = visual->red_mask; + cube->u.true_color.color_mask[1] = visual->green_mask; + cube->u.true_color.color_mask[2] = visual->blue_mask; + return cube; + } + else if(visual->class == StaticGray || visual->class == GrayScale) goto failure; + + /* else PseudoColor (or StaticColor or DirectColor: do they exist?) */ + + if(visual->class == PseudoColor && visual->map_entries < 8) goto failure; + + /* TODO get a standard colormap (if there is one) from the Xmu routines */ + + if(depth > 8 || getenv("CHIMERA_AGGRESSIVE_COLORS")) + { + if(lf_attempt_axbxc(cube, display, colormap, 6, 10, 4)) + return cube; + if(lf_attempt_axbxc(cube, display, colormap, 7, 9, 4)) + return cube; + if(lf_attempt_axbxc(cube, display, colormap, 6, 9, 4)) + return cube; + if(lf_attempt_axbxc(cube, display, colormap, 6, 8, 4)) + return cube; + if(lf_attempt_axbxc(cube, display, colormap, 6, 8, 4)) + return cube; + if(lf_attempt_axbxc(cube, display, colormap, 5, 10, 4)) + return cube; + if(lf_attempt_axbxc(cube, display, colormap, 5, 9, 4)) + return cube; + if(lf_attempt_axbxc(cube, display, colormap, 4, 10, 4)) + return cube; + if(lf_attempt_axbxc(cube, display, colormap, 4, 9, 4)) + return cube; + } + if(depth == 8) + { + /* share with xv */ + if(lf_attempt_axbxc(cube, display, colormap, 4, 8, 4)) + return cube; + /* share with ghostscript */ + if(lf_attempt_axbxc(cube, display, colormap, 5, 5, 5)) + return cube; + /* share with old versions of xv and N******e */ + if(lf_attempt_axbxc(cube, display, colormap, 6, 6, 6)) + return cube; + /* share with N*******e -ncols 64 */ + if(lf_attempt_axbxc(cube, display, colormap, 4, 4, 4)) + return cube; + /* getting desperate */ + if(lf_attempt_axbxc(cube, display, colormap, 4, 6, 4)) + return cube; + if(lf_attempt_axbxc(cube, display, colormap, 3, 5, 3)) + return cube; + if(lf_attempt_axbxc(cube, display, colormap, 3, 4, 3)) + return cube; + if(lf_attempt_axbxc(cube, display, colormap, 3, 3, 3)) + return cube; + if(lf_attempt_axbxc(cube, display, colormap, 2, 5, 2)) + return cube; + } + if(lf_attempt_axbxc(cube, display, colormap, 2, 4, 2)) + return cube; + /* almost total desperation */ + if(lf_attempt_axbxc(cube, display, colormap, 2, 3, 2)) + return cube; + /* total desperation */ + if(lf_attempt_axbxc(cube, display, colormap, 2, 2, 2)) + return cube; + + failure: + free(cube); + (*listextension)->cube = 0; + return 0; +} + + +static bool +lf_get_gray( + Display *display, + Colormap colormap, + cct_cube cube, + int n) +{ + XColor try; + int status; + try.red = cube->u.grayscale.brightnesses[n] << 8; + try.green = cube->u.grayscale.brightnesses[n] << 8; + try.blue = cube->u.grayscale.brightnesses[n] << 8; + try.flags = DoRed | DoGreen | DoBlue; + status = XAllocColor(display, colormap, &try); + if(!status) return false; + cube->u.grayscale.pixel_values[n] = try.pixel; + cube->u.grayscale.brightnesses[n] = try.green >> 8; + return true; +} + +static int +lf_reverse_byte(int a) +{ + return ((a & 128) >> 7) | + ((a & 64) >> 5) | + ((a & 32) >> 3) | + ((a & 16) >> 1) | + ((a & 8) << 1) | + ((a & 4) << 3) | + ((a & 2) << 5) | + ((a & 1) << 7); +} + +/* + * The Window parameter is only needed to allow the Visual to be + * determined + */ + +cct_cube +xccf_allocate_grays( + Display *display, + Colormap colormap, + Visual *visual, + int depth) +{ + cct_cube cube; + lt_cubealist liststepper; + lt_cubealist *listextension; + int n = 0; + int maxcols, i, j; + + /* + * Make sure that the color cube is always allocated before the gray + * colormap entries + */ + xccf_allocate_cube(display, colormap, visual, depth); + + for(liststepper = lv_grayalist, listextension = &lv_grayalist; + liststepper; + listextension = &(liststepper->next), liststepper = liststepper->next) + { + if(liststepper->display == display && liststepper->colormap == colormap) + return liststepper->cube; + } + + (*listextension) = (lt_cubealist)calloc(1, sizeof(struct ls_cubealist)); + (*listextension)->display = display; + (*listextension)->colormap = colormap; + cube = (cct_cube)calloc(1, sizeof(struct ccs_cube)); + (*listextension)->cube = cube; + + /* + * We are really saving grays here. With only 16 grays we will dither, not + * just remap. + */ + if(visual->class != PseudoColor || depth > 8) + maxcols = 256; + else + maxcols = 16; + + if(visual->class == PseudoColor && visual->map_entries < 8) + maxcols = 3; + + /* TODO get a standard colormap (if there is one) from the Xmu routines */ + + cube->u.grayscale.brightnesses[0] = 0; + cube->u.grayscale.brightnesses[1] = 255; + if(!lf_get_gray(display, colormap, cube, 0)) goto failure; + n = 1; + if(!lf_get_gray(display, colormap, cube, 1)) goto failure; + n = 2; + for(i = 1; i < 255 && n < maxcols; i++, n++) + { + cube->u.grayscale.brightnesses[n] = lf_reverse_byte(i); + if(!lf_get_gray(display, colormap, cube, n)) + { + n--; + } + } + + cube->u.grayscale.value_count = n; + + if(getenv("CHIMERA_TEST_COLOR_ALLOCATION")) + printf("Allocated %d grays\n", n); + + /* + * Bubble sort (I know...) + */ + { + bool something_happened; + do + { + something_happened = false; + for(i = 1; i < n; i++) + { + if(cube->u.grayscale.brightnesses[i] < + cube->u.grayscale.brightnesses[i-1]) + { + unsigned long t; + t = cube->u.grayscale.brightnesses[i]; + cube->u.grayscale.brightnesses[i] = + cube->u.grayscale.brightnesses[i-1]; + cube->u.grayscale.brightnesses[i-1] = t; + t = cube->u.grayscale.pixel_values[i]; + cube->u.grayscale.pixel_values[i] = + cube->u.grayscale.pixel_values[i-1]; + cube->u.grayscale.pixel_values[i-1] = t; + something_happened = true; + } + } + for(i = n-1; i; i--) + { + if(cube->u.grayscale.brightnesses[i] < + cube->u.grayscale.brightnesses[i-1]) + { + unsigned long t; + t = cube->u.grayscale.brightnesses[i]; + cube->u.grayscale.brightnesses[i] = + cube->u.grayscale.brightnesses[i-1]; + cube->u.grayscale.brightnesses[i-1] = t; + t = cube->u.grayscale.pixel_values[i]; + cube->u.grayscale.pixel_values[i] = + cube->u.grayscale.pixel_values[i-1]; + cube->u.grayscale.pixel_values[i-1] = t; + something_happened = true; + } + } + } while(something_happened); + } + + /* + * Remove duplicates + */ + for(j = i = 0; i < n; i++) + { + if(cube->u.grayscale.brightnesses[i] != cube->u.grayscale.brightnesses[j]) + { + j++; + } + cube->u.grayscale.brightnesses[j] = cube->u.grayscale.brightnesses[i]; + cube->u.grayscale.pixel_values[j] = cube->u.grayscale.pixel_values[i]; + } + + n = j + 1; + + cube->u.grayscale.value_count = n; + + if(getenv("CHIMERA_TEST_COLOR_ALLOCATION")) + printf("That was %d unique grays\n", n); + + return cube; + + failure: + if(n) + { + XFreeColors(display, colormap, cube->u.grayscale.pixel_values, n, 0); + } + free(cube); + (*listextension)->cube = 0; + return 0; +} + +static int lf_abs(int a) +{ + if(a >= 0) return a; + return -a; +} + +static int +lf_get_color_index( + unsigned short xp_intensity, + unsigned int *brightnesses, + int value_count) +{ + int i; + int intensity = xp_intensity >> 8; + int answer = 0; + int best_brightness = intensity; + for (i = 0; i < value_count; i++) + { + int bs = brightnesses[i]; + if(lf_abs(bs - intensity) < best_brightness) + { + best_brightness = lf_abs(bs - intensity); + answer = i; + } + } + return answer; +} + + +#ifdef __GNUC__ +__inline__ +#endif +bool +xccf_color_compare( + int r1, + int g1, + int b1, + int r2, + int g2, + int b2) +{ + if(lf_abs(r1 - r2) > 6) return false; + if(lf_abs(g1 - g2) > 3) return false; + if(lf_abs(b1 - b2) > 6) return false; + return true; +} + + +bool +xccf_allocate_special( + Display *display, + Colormap colormap, + cct_cube cube, + cct_cube grayscale, + unsigned short intensities[3], + bool *really_allocated_return, + bool dont_really_allocate, + cct_special_color specials, + int special_count, + unsigned long *special_return) +{ + int i; + int r = intensities[0] >> 8; + int g = intensities[1] >> 8; + int b = intensities[2] >> 8; + int gr; + if(really_allocated_return) *really_allocated_return = false; + if(cube) + { + switch(cube->cube_type) + { + case cube_mapping: + case cube_no_mapping: + { + int color_index[3]; + for(i = 0; i < 3; i++) + color_index[i] = + lf_get_color_index(intensities[i], + cube->u.no_mapping.brightnesses[i], + cube->u.no_mapping.value_count[i]); + if(xccf_color_compare( + cube->u.no_mapping.brightnesses[0][color_index[0]], + cube->u.no_mapping.brightnesses[1][color_index[1]], + cube->u.no_mapping.brightnesses[2][color_index[2]], + r, + g, + b)) + { + *special_return = + cube->u.no_mapping.pixel_values[0][color_index[0]] + + cube->u.no_mapping.pixel_values[1][color_index[1]] + + cube->u.no_mapping.pixel_values[2][color_index[2]]; + if(cube->cube_type == cube_mapping) + *special_return = + cube->u.mapping.mapping[*special_return]; + return true; + } + break; + } + case cube_true_color: + case cube_grayscale: + break; + } + } + gr = (r + g + b) / 3; + if(grayscale /* && xccf_color_compare(r,g,b,gr,gr,gr)*/) + { + for(i = 0; i < grayscale->u.grayscale.value_count; i++) + { + if(xccf_color_compare(grayscale->u.grayscale.brightnesses[i], + grayscale->u.grayscale.brightnesses[i], + grayscale->u.grayscale.brightnesses[i], + r, + g, + b)) + { + *special_return = grayscale->u.grayscale.pixel_values[i]; + return true; + } + } + } + for(i = 0; i < special_count; i++) + { + if(xccf_color_compare(specials[i].brightnesses[0], + specials[i].brightnesses[1], + specials[i].brightnesses[2], + r, + g, + b)) + { + *special_return = specials[i].pixel; + return true; + } + } + if(!dont_really_allocate) + { + XColor try; + int status; + try.red = intensities[0]; + try.green = intensities[1]; + try.blue = intensities[2]; + try.flags = DoRed | DoGreen | DoBlue; + status = XAllocColor(display, colormap, &try); + if(status) + { + if(!xccf_color_compare( + intensities[0] >> 8, intensities[1] >> 8, + intensities[2] >> 8, try.red >> 8, try.green >> 8, + try.blue >> 8)) + { + /* The server gave us a color but it was a really bad match */ + XFreeColors(display, colormap, &try.pixel, 1, 0); + } + else + { + /* The server gave us a color that worked. */ + *special_return = try.pixel; + specials[special_count].pixel = try.pixel; + specials[special_count].brightnesses[0] = try.red >> 8; + specials[special_count].brightnesses[1] = try.green >> 8; + specials[special_count].brightnesses[2] = try.blue >> 8; + if(really_allocated_return) *really_allocated_return = true; + return true; + } + } + } + return false; +} diff --git a/image/xcolorcube.h b/image/xcolorcube.h new file mode 100644 index 0000000..5e160d3 --- /dev/null +++ b/image/xcolorcube.h @@ -0,0 +1,80 @@ +/* xcolorcube.h + * + * (c) 1995 Erik Corry. ehcorry@inet.uni-c.dk erik@kroete2.freinet.de + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +#ifndef XCOLORCUBE_H_INCLUDED +#define XCOLORCUBE_H_INCLUDED + +#include "colorcube.h" + +/* + * Allocates the best possible color cuboid for the given colormap + * and display. If you ask for a cuboid for a display/colormap that + * was already asked for it returns the cached value from last time + * so don't free the return value without thinking about it. + * Should be able to cope with a program displaying on several different + * displays + */ + +cct_cube +xccf_allocate_cube( + Display *display, + Colormap colormap, + Visual *visual, + int depth); + +/* + * Allocates a decent number of gray colormap entries on the colormap + * and display. If you ask for grays for a display/colormap that + * was already asked for it returns the cached value from last time + * so don't free the return value without thinking about it. + * Should be able to cope with a program displaying on several different + * displays + */ + +cct_cube +xccf_allocate_grays( + Display *display, + Colormap colormap, + Visual *visual, + int depth); + +bool +xccf_allocate_special( + Display *display, + Colormap colormap, + cct_cube cube, + cct_cube grayscale, + unsigned short intensities[3], + bool *really_allocated_return, + bool dont_really_allocate, + cct_special_color specials, + int special_count, + unsigned long *special_return); + +bool +xccf_color_compare( + int r1, + int g1, + int b1, + int r2, + int g2, + int b2); + + +#endif /* XCOLORCUBE_H_INCLUDED */ diff --git a/mxw/AuthDialog.c b/mxw/AuthDialog.c new file mode 100644 index 0000000..a1de560 --- /dev/null +++ b/mxw/AuthDialog.c @@ -0,0 +1,450 @@ +/* Modified by John */ + +/*********************************************************** + +Copyright (c) 1987, 1988, 1994 X Consortium + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of the X Consortium shall not be +used in advertising or otherwise to promote the sale, use or other dealings +in this Software without prior written authorization from the X Consortium. + + +Copyright 1987, 1988 by Digital Equipment Corporation, Maynard, Massachusetts. + + All Rights Reserved + +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose and without fee is hereby granted, +provided that the above copyright notice appear in all copies and that +both that copyright notice and this permission notice appear in +supporting documentation, and that the name of Digital not be +used in advertising or publicity pertaining to distribution of the +software without specific, written prior permission. + +DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING +ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL +DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR +ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS +SOFTWARE. + +******************************************************************/ + +/* NOTE: THIS IS NOT A WIDGET! Rather, this is an interface to a widget. + It implements policy, and gives a (hopefully) easier-to-use interface + than just directly making your own form. */ + + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "TextField.h" + +#include "AuthDialogP.h" + +/* + * After we have set the string in the value widget we set the + * string to a magic value. So that when a SetValues request is made + * on the mydialog value we will notice it, and reset the string. + */ +#define MAGIC_VALUE ((char *) 3) + +#define streq(a,b) (strcmp( (a), (b) ) == 0) + +static XtResource resources[] = { + { XtNlabel, XtCLabel, XtRString, sizeof(String), + XtOffsetOf(AuthDialogRec, authdialog.label), XtRString, NULL }, + { "username", XtCValue, XtRString, sizeof(String), + XtOffsetOf(AuthDialogRec, authdialog.username), XtRString, NULL }, + { XtNicon, XtCIcon, XtRBitmap, sizeof(Pixmap), + XtOffsetOf(AuthDialogRec, authdialog.icon), XtRImmediate, 0 }, + { XtNcallback, XtCCallback, XtRCallback, sizeof(XtPointer), + XtOffsetOf(AuthDialogRec, authdialog.callbacks),XtRCallback, NULL }, +}; + +static void AuthInitialize(), AuthConstraintInitialize(); +static void CreateAuthDialogFields(); +static void UsernameActivate(); +static void PasswordActivate(); + +static Boolean SetValues(); + +AuthDialogClassRec authdialogClassRec = { + { /* core_class fields */ + /* superclass */ (WidgetClass) &formClassRec, + /* class_name */ "AuthDialog", + /* widget_size */ sizeof(AuthDialogRec), + /* class_initialize */ XawInitializeWidgetSet, + /* class_part init */ NULL, + /* class_inited */ FALSE, + /* initialize */ AuthInitialize, + /* initialize_hook */ NULL, + /* realize */ XtInheritRealize, + /* actions */ NULL, + /* num_actions */ 0, + /* resources */ resources, + /* num_resources */ XtNumber(resources), + /* xrm_class */ NULLQUARK, + /* compress_motion */ TRUE, + /* compress_exposure */ TRUE, + /* compress_enterleave*/ TRUE, + /* visible_interest */ FALSE, + /* destroy */ NULL, + /* resize */ XtInheritResize, + /* expose */ XtInheritExpose, + /* set_values */ SetValues, + /* set_values_hook */ NULL, + /* set_values_almost */ XtInheritSetValuesAlmost, + /* get_values_hook */ NULL, + /* accept_focus */ NULL, + /* version */ XtVersion, + /* callback_private */ NULL, + /* tm_table */ NULL, + /* query_geometry */ XtInheritQueryGeometry, + /* display_accelerator*/ XtInheritDisplayAccelerator, + /* extension */ NULL + }, + { /* composite_class fields */ + /* geometry_manager */ XtInheritGeometryManager, + /* change_managed */ XtInheritChangeManaged, + /* insert_child */ XtInheritInsertChild, + /* delete_child */ XtInheritDeleteChild, + /* extension */ NULL + }, + { /* constraint_class fields */ + /* subresourses */ NULL, + /* subresource_count */ 0, + /* constraint_size */ sizeof(AuthDialogConstraintsRec), + /* initialize */ AuthConstraintInitialize, + /* destroy */ NULL, + /* set_values */ NULL, + /* extension */ NULL + }, + { /* form_class fields */ + /* layout */ XtInheritLayout + }, + { /* authdialog_class fields */ + /* empty */ 0 + } +}; + +WidgetClass authdialogWidgetClass = (WidgetClass)&authdialogClassRec; + +/* ARGSUSED */ +static void AuthInitialize(request, new, args, num_args) +Widget request, new; +ArgList args; +Cardinal *num_args; +{ + AuthDialogWidget dw = (AuthDialogWidget)new; + Arg arglist[9]; + Cardinal arg_cnt = 0; + + XtSetArg(arglist[arg_cnt], XtNborderWidth, 0); arg_cnt++; + XtSetArg(arglist[arg_cnt], XtNleft, XtChainLeft); arg_cnt++; + + if (dw->authdialog.icon != (Pixmap)0) { + XtSetArg(arglist[arg_cnt], XtNbitmap, dw->authdialog.icon); arg_cnt++; + XtSetArg(arglist[arg_cnt], XtNright, XtChainLeft); arg_cnt++; + dw->authdialog.iconW = + XtCreateManagedWidget( "icon", labelWidgetClass, + new, arglist, arg_cnt ); + arg_cnt = 2; + XtSetArg(arglist[arg_cnt], XtNfromHoriz, dw->authdialog.iconW); + arg_cnt++; + } else dw->authdialog.iconW = (Widget)NULL; + + XtSetArg(arglist[arg_cnt], XtNlabel, dw->authdialog.label); arg_cnt++; + XtSetArg(arglist[arg_cnt], XtNright, XtChainRight); arg_cnt++; + + dw->authdialog.labelW = XtCreateManagedWidget( "label", labelWidgetClass, + new, arglist, arg_cnt); + + if (dw->authdialog.iconW != (Widget)NULL && + (dw->authdialog.labelW->core.height < + dw->authdialog.iconW->core.height)) { + XtSetArg( arglist[0], XtNheight, dw->authdialog.iconW->core.height ); + XtSetValues( dw->authdialog.labelW, arglist, ONE ); + } + CreateAuthDialogFields( (Widget) dw); +} + +/* ARGSUSED */ +static void AuthConstraintInitialize(request, new, args, num_args) +Widget request, new; +ArgList args; +Cardinal *num_args; +{ + AuthDialogWidget dw = (AuthDialogWidget)new->core.parent; + AuthDialogConstraints constraint = + (AuthDialogConstraints)new->core.constraints; + + if (!XtIsSubclass(new, commandWidgetClass)) /* if not a button */ + return; /* then just use defaults */ + + constraint->form.left = constraint->form.right = XtChainLeft; + constraint->form.vert_base = dw->authdialog.passwordW; + + if (dw->composite.num_children > 1) { + WidgetList children = dw->composite.children; + Widget *childP; + for (childP = children + dw->composite.num_children - 1; + childP >= children; childP-- ) { + if (*childP == dw->authdialog.labelW || + *childP == dw->authdialog.passwordW) + break; + if (XtIsManaged(*childP) && + XtIsSubclass(*childP, commandWidgetClass)) { + constraint->form.horiz_base = *childP; + break; + } + } + } +} + +#define ICON 0 +#define LABEL 1 +#define NUM_CHECKS 2 + +/* ARGSUSED */ +static Boolean SetValues(current, request, new, in_args, in_num_args) +Widget current, request, new; +ArgList in_args; +Cardinal *in_num_args; +{ + AuthDialogWidget w = (AuthDialogWidget)new; + AuthDialogWidget old = (AuthDialogWidget)current; + Arg args[5]; + Cardinal num_args; + int i; + Boolean checks[NUM_CHECKS]; + + for (i = 0; i < NUM_CHECKS; i++) + checks[i] = FALSE; + + for (i = 0; i < *in_num_args; i++) { + if (streq(XtNicon, in_args[i].name)) + checks[ICON] = TRUE; + if (streq(XtNlabel, in_args[i].name)) + checks[LABEL] = TRUE; + } + + if (checks[ICON]) { + if (w->authdialog.icon != (Pixmap)0) { + XtSetArg( args[0], XtNbitmap, w->authdialog.icon ); + if (old->authdialog.iconW != (Widget)NULL) { + XtSetValues( old->authdialog.iconW, args, ONE ); + } else { + XtSetArg( args[1], XtNborderWidth, 0); + XtSetArg( args[2], XtNleft, XtChainLeft); + XtSetArg( args[3], XtNright, XtChainLeft); + w->authdialog.iconW = + XtCreateWidget( "icon", labelWidgetClass, + new, args, FOUR ); + ((AuthDialogConstraints) + w->authdialog.labelW->core.constraints)-> + form.horiz_base = w->authdialog.iconW; + XtManageChild(w->authdialog.iconW); + } + } else if (old->authdialog.icon != (Pixmap)0) { + ((AuthDialogConstraints) + w->authdialog.labelW->core.constraints)-> + form.horiz_base = (Widget)NULL; + XtDestroyWidget(old->authdialog.iconW); + w->authdialog.iconW = (Widget)NULL; + } + } + + if ( checks[LABEL] ) { + num_args = 0; + XtSetArg( args[num_args], XtNlabel, w->authdialog.label ); num_args++; + if (w->authdialog.iconW != (Widget)NULL && + (w->authdialog.labelW->core.height <= + w->authdialog.iconW->core.height)) { + XtSetArg(args[num_args], XtNheight, + w->authdialog.iconW->core.height); + num_args++; + } + XtSetValues( w->authdialog.labelW, args, num_args ); + } + + if ( w->authdialog.username != old->authdialog.username ) { + TextFieldSetString(w->authdialog.usernameW, w->authdialog.username); + w->authdialog.username = MAGIC_VALUE; + } + + return False; +} + +/* Function Name: CreateAuthDialogFields + * Description: Creates the authdialog widgets username and password + * widgets. + * Arguments: w - the authdialog widget. + * Returns: none. + * + * must be called only when w->authdialog.value is non-nil. + */ + +static void +CreateAuthDialogFields(w) +Widget w; +{ + AuthDialogWidget dw = (AuthDialogWidget) w; + + dw->authdialog.usernameW = + XtVaCreateWidget("username", + textfieldWidgetClass, w, + XtNstring, dw->authdialog.username, + XtNfromVert, dw->authdialog.labelW, + XtNleft, XtChainLeft, + XtNright, XtChainRight, + NULL); + XtAddCallback(dw->authdialog.usernameW, XtNactivateCallback, + UsernameActivate, dw); + + dw->authdialog.passwordW = + XtVaCreateWidget("password", + textfieldWidgetClass, w, + XtNecho, False, + XtNfromVert, dw->authdialog.usernameW, + XtNleft, XtChainLeft, + XtNright, XtChainRight, + NULL); + XtAddCallback(dw->authdialog.passwordW, XtNactivateCallback, + PasswordActivate, dw); + + /* if the value widget is being added after buttons, + * then the buttons need new layout constraints. + */ + if (dw->composite.num_children > 1) { + WidgetList children = dw->composite.children; + Widget *childP; + for (childP = children + dw->composite.num_children - 1; + childP >= children; childP-- ) { + if (*childP == dw->authdialog.labelW || + *childP == dw->authdialog.usernameW || + *childP == dw->authdialog.passwordW) + continue; + if (XtIsManaged(*childP) && + XtIsSubclass(*childP, commandWidgetClass)) { + ((AuthDialogConstraints)(*childP)->core.constraints)-> + form.vert_base = dw->authdialog.passwordW; + } + } + } + + XtManageChild(dw->authdialog.usernameW); + XtManageChild(dw->authdialog.passwordW); + +/* + * Value widget gets the keyboard focus. + */ + + XtSetKeyboardFocus(w, dw->authdialog.usernameW); + dw->authdialog.username = MAGIC_VALUE; +} + + +void +#if NeedFunctionPrototypes +AuthDialogAddButton(Widget authdialog, _Xconst char* name, XtCallbackProc function, + XtPointer param) +#else +AuthDialogAddButton(authdialog, name, function, param) +Widget authdialog; +String name; +XtCallbackProc function; +XtPointer param; +#endif +{ +/* + * Correct Constraints are all set in AuthConstraintInitialize(). + */ + Widget button; + + button = XtCreateManagedWidget( name, commandWidgetClass, authdialog, + (ArgList)NULL, (Cardinal)0 ); + + if (function != NULL) /* don't add NULL callback func. */ + XtAddCallback(button, XtNcallback, function, param); +} + + +char * +#if NeedFunctionPrototypes +AuthDialogGetUsername(Widget w) +#else +AuthDialogGetUsername(w) +Widget w; +#endif +{ + return(TextFieldGetString(((AuthDialogWidget)w)->authdialog.usernameW)); +} + +char * +#if NeedFunctionPrototypes +AuthDialogGetPassword(Widget w) +#else +AuthDialogGetPassword(w) +Widget w; +#endif +{ + return(TextFieldGetString(((AuthDialogWidget)w)->authdialog.passwordW)); +} + +/* + * UsernameActivate + */ +static void +UsernameActivate(w, cldata, cbdata) +Widget w; +XtPointer cldata, cbdata; +{ + AuthDialogWidget dw; + + dw = (AuthDialogWidget)XtParent(w); + XtSetKeyboardFocus((Widget)dw, dw->authdialog.passwordW); + + return; +} + +/* + * PasswordActivate + */ +static void +PasswordActivate(w, cldata, cbdata) +Widget w; +XtPointer cldata, cbdata; +{ + AuthDialogWidget dw; + + dw = (AuthDialogWidget)XtParent(w); + XtCallCallbackList(XtParent(w), dw->authdialog.callbacks, (XtPointer) NULL); + + return; +} diff --git a/mxw/AuthDialog.h b/mxw/AuthDialog.h new file mode 100644 index 0000000..d6ee4be --- /dev/null +++ b/mxw/AuthDialog.h @@ -0,0 +1,111 @@ +/* Modified by John */ + +/*********************************************************** + +Copyright (c) 1987, 1988, 1994 X Consortium + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of the X Consortium shall not be +used in advertising or otherwise to promote the sale, use or other dealings +in this Software without prior written authorization from the X Consortium. + + +Copyright 1987, 1988 by Digital Equipment Corporation, Maynard, Massachusetts. + + All Rights Reserved + +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose and without fee is hereby granted, +provided that the above copyright notice appear in all copies and that +both that copyright notice and this permission notice appear in +supporting documentation, and that the name of Digital not be +used in advertising or publicity pertaining to distribution of the +software without specific, written prior permission. + +DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING +ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL +DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR +ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS +SOFTWARE. + +******************************************************************/ + +#ifndef _AuthDialog_h +#define _AuthDialog_h + +#include + +/*********************************************************************** + * + * AuthDialog Widget + * + ***********************************************************************/ + +/* Parameters: + + Name Class RepType Default Value + ---- ----- ------- ------------- + background Background Pixel XtDefaultBackground + border BorderColor Pixel XtDefaultForeground + borderWidth BorderWidth Dimension 1 + destroyCallback Callback Pointer NULL + height Height Dimension computed at create + icon Icon Pixmap 0 + label Label String NULL + mappedWhenManaged MappedWhenManaged Boolean True + sensitive Sensitive Boolean True + value Value String NULL + width Width Dimension computed at create + x Position Position 0 + y Position Position 0 + +*/ + +#define XtCIcon "Icon" +#define XtNicon "icon" + +typedef struct _AuthDialogClassRec *AuthDialogWidgetClass; +typedef struct _AuthDialogRec *AuthDialogWidget; + +extern WidgetClass authdialogWidgetClass; + +extern void AuthDialogAddButton( +#if NeedFunctionPrototypes + Widget /* authdialog */, + _Xconst char* /* name */, + XtCallbackProc /* function */, + XtPointer /* client_data */ +#endif +); + +extern char *AuthDialogGetUsername( +#if NeedFunctionPrototypes + Widget /* w */ +#endif +); + +extern char *AuthDialogGetPassword( +#if NeedFunctionPrototypes + Widget /* w */ +#endif +); + +#endif /* _AuthDialog_h */ diff --git a/mxw/AuthDialogP.h b/mxw/AuthDialogP.h new file mode 100644 index 0000000..8940bff --- /dev/null +++ b/mxw/AuthDialogP.h @@ -0,0 +1,99 @@ +/* Modified by john */ + +/*********************************************************** + +Copyright (c) 1987, 1988 X Consortium + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of the X Consortium shall not be +used in advertising or otherwise to promote the sale, use or other dealings +in this Software without prior written authorization from the X Consortium. + + +Copyright 1987, 1988 by Digital Equipment Corporation, Maynard, Massachusetts. + + All Rights Reserved + +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose and without fee is hereby granted, +provided that the above copyright notice appear in all copies and that +both that copyright notice and this permission notice appear in +supporting documentation, and that the name of Digital not be +used in advertising or publicity pertaining to distribution of the +software without specific, written prior permission. + +DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING +ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL +DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR +ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS +SOFTWARE. + +******************************************************************/ + +/* Private definitions for AuthDialog widget */ + +#ifndef _AuthDialogP_h +#define _AuthDialogP_h + +#include "AuthDialog.h" +#include + +typedef struct {int empty;} AuthDialogClassPart; + +typedef struct _AuthDialogClassRec { + CoreClassPart core_class; + CompositeClassPart composite_class; + ConstraintClassPart constraint_class; + FormClassPart form_class; + AuthDialogClassPart authdialog_class; +} AuthDialogClassRec; + +extern AuthDialogClassRec authdialogClassRec; + +typedef struct _AuthDialogPart { + /* resources */ + String label; /* description of the authdialog */ + String username; /* username string */ + Pixmap icon; /* icon bitmap */ + XtCallbackList callbacks; /* callbacks */ + /* private data */ + Widget iconW; /* widget to display the icon */ + Widget labelW; /* widget to display description*/ + Widget usernameW; /* username textfield */ + Widget passwordW; /* password textfield */ +} AuthDialogPart; + +typedef struct _AuthDialogRec { + CorePart core; + CompositePart composite; + ConstraintPart constraint; + FormPart form; + AuthDialogPart authdialog; +} AuthDialogRec; + +typedef struct {int empty;} AuthDialogConstraintsPart; + +typedef struct _AuthDialogConstraintsRec { + FormConstraintsPart form; + AuthDialogConstraintsPart authdialog; +} AuthDialogConstraintsRec, *AuthDialogConstraints; + +#endif /* _AuthDialogP_h */ diff --git a/mxw/Imakefile b/mxw/Imakefile new file mode 100644 index 0000000..53e32ba --- /dev/null +++ b/mxw/Imakefile @@ -0,0 +1,16 @@ +#include <../Common.tmpl> + +HEADERS = MyDialog.h MyDialogP.h TextField.h TextFieldP.h AuthDialogP.h \ + AuthDialog.h + +SRCS = MyDialog.c TextField.c AuthDialog.c +OBJS = MyDialog.o TextField.o AuthDialog.o + +EXTRA_INCLUDES = -I./ -I../port +EXTRA_DEFINES = $(CHIMERA_DEFINES) + +NormalLibraryTarget(mxw, $(OBJS)) + +DependTarget() + +install.man:: diff --git a/mxw/MyDialog.c b/mxw/MyDialog.c new file mode 100644 index 0000000..f237287 --- /dev/null +++ b/mxw/MyDialog.c @@ -0,0 +1,340 @@ +/* Modified by John */ + +/*********************************************************** + +Copyright (c) 1987, 1988, 1994 X Consortium + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of the X Consortium shall not be +used in advertising or otherwise to promote the sale, use or other dealings +in this Software without prior written authorization from the X Consortium. + + +Copyright 1987, 1988 by Digital Equipment Corporation, Maynard, Massachusetts. + + All Rights Reserved + +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose and without fee is hereby granted, +provided that the above copyright notice appear in all copies and that +both that copyright notice and this permission notice appear in +supporting documentation, and that the name of Digital not be +used in advertising or publicity pertaining to distribution of the +software without specific, written prior permission. + +DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING +ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL +DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR +ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS +SOFTWARE. + +******************************************************************/ + +/* NOTE: THIS IS NOT A WIDGET! Rather, this is an interface to a widget. + It implements policy, and gives a (hopefully) easier-to-use interface + than just directly making your own form. */ + + +#include +#include +#include +#include + +#include +#include +#include + +#include "TextField.h" + +#include "MyDialogP.h" + +static char defaultTranslations[] = +"Return: MyPressReturn() \n\ + Ctrlm: MyPressReturn() \n"; + +static XtResource resources[] = { + { XtNlabel, XtCLabel, XtRString, sizeof(String), + XtOffsetOf(MyDialogRec, mydialog.label), XtRString, NULL }, + {XtNcallback, XtCCallback, XtRCallback, sizeof(XtPointer), + XtOffsetOf(MyDialogRec, mydialog.callbacks),XtRCallback,(XtPointer)NULL}, +}; + +static void Initialize(), ConstraintInitialize(), MyPressReturn(); +static Boolean SetValues(); + +static XtActionsRec actionsList[] = +{ + { "MyPressReturn", MyPressReturn } +}; + +MyDialogClassRec mydialogClassRec = { + { /* core_class fields */ + /* superclass */ (WidgetClass) &formClassRec, + /* class_name */ "MyDialog", + /* widget_size */ sizeof(MyDialogRec), + /* class_initialize */ XawInitializeWidgetSet, + /* class_part init */ NULL, + /* class_inited */ FALSE, + /* initialize */ Initialize, + /* initialize_hook */ NULL, + /* realize */ XtInheritRealize, + /* actions */ actionsList, + /* num_actions */ XtNumber(actionsList), + /* resources */ resources, + /* num_resources */ XtNumber(resources), + /* xrm_class */ NULLQUARK, + /* compress_motion */ TRUE, + /* compress_exposure */ TRUE, + /* compress_enterleave*/ TRUE, + /* visible_interest */ FALSE, + /* destroy */ NULL, + /* resize */ XtInheritResize, + /* expose */ XtInheritExpose, + /* set_values */ SetValues, + /* set_values_hook */ NULL, + /* set_values_almost */ XtInheritSetValuesAlmost, + /* get_values_hook */ NULL, + /* accept_focus */ NULL, + /* version */ XtVersion, + /* callback_private */ NULL, + /* tm_table */ NULL, + /* query_geometry */ XtInheritQueryGeometry, + /* display_accelerator*/ XtInheritDisplayAccelerator, + /* extension */ NULL + }, + { /* composite_class fields */ + /* geometry_manager */ XtInheritGeometryManager, + /* change_managed */ XtInheritChangeManaged, + /* insert_child */ XtInheritInsertChild, + /* delete_child */ XtInheritDeleteChild, + /* extension */ NULL + }, + { /* constraint_class fields */ + /* subresourses */ NULL, + /* subresource_count */ 0, + /* constraint_size */ sizeof(MyDialogConstraintsRec), + /* initialize */ ConstraintInitialize, + /* destroy */ NULL, + /* set_values */ NULL, + /* extension */ NULL + }, + { /* form_class fields */ + /* layout */ XtInheritLayout + }, + { /* mydialog_class fields */ + /* empty */ 0 + } +}; + +WidgetClass mydialogWidgetClass = (WidgetClass)&mydialogClassRec; + +/* ARGSUSED */ +static void Initialize(request, new, args, num_args) +Widget request, new; +ArgList args; +Cardinal *num_args; +{ + MyDialogWidget dw = (MyDialogWidget)new; + + dw->mydialog.messageW = + XtVaCreateManagedWidget("mydialog_message", + textfieldWidgetClass, + new, + XtNstring, dw->mydialog.label, + XtNdisplayCaret, False, + XtNeditable, False, + XtNborderWidth, 0, + XtNleft, XtChainLeft, + XtNright, XtChainRight, + NULL); + + + dw->mydialog.valueW = XtVaCreateWidget("mydialog_value", + textfieldWidgetClass, + new, + XtNfromVert, dw->mydialog.messageW, + XtNleft, XtChainLeft, + XtNright, XtChainRight, + NULL); + + /* + * if the value widget is being added after buttons, + * then the buttons need new layout constraints. + */ + if (dw->composite.num_children > 1) { + WidgetList children = dw->composite.children; + Widget *childP; + + for (childP = children + dw->composite.num_children - 1; + childP >= children; childP-- ) { + if (*childP == dw->mydialog.messageW || + *childP == dw->mydialog.messageW) + continue; + if (XtIsManaged(*childP) && + XtIsSubclass(*childP, commandWidgetClass)) { + ((MyDialogConstraints)(*childP)->core.constraints)-> + form.vert_base = dw->mydialog.messageW; + } + } + } + + XtOverrideTranslations(dw->mydialog.valueW, + XtParseTranslationTable(defaultTranslations)); + + XtManageChild(dw->mydialog.valueW); + + XtSetKeyboardFocus(new, dw->mydialog.valueW); + } + +/* ARGSUSED */ +static void ConstraintInitialize(request, new, args, num_args) +Widget request, new; +ArgList args; +Cardinal *num_args; +{ + MyDialogWidget dw = (MyDialogWidget)new->core.parent; + MyDialogConstraints constraint = (MyDialogConstraints)new->core.constraints; + + if (!XtIsSubclass(new, commandWidgetClass)) /* if not a button */ + return; /* then just use defaults */ + + constraint->form.left = constraint->form.right = XtChainLeft; + constraint->form.vert_base = dw->mydialog.valueW; + + if (dw->composite.num_children > 1) { + WidgetList children = dw->composite.children; + Widget *childP; + for (childP = children + dw->composite.num_children - 1; + childP >= children; childP-- ) { + if (*childP == dw->mydialog.messageW || + *childP == dw->mydialog.valueW) + break; + if (XtIsManaged(*childP) && + XtIsSubclass(*childP, commandWidgetClass)) { + constraint->form.horiz_base = *childP; + break; + } + } + } +} + +/* ARGSUSED */ +static Boolean +SetValues(current, request, new, in_args, in_num_args) +Widget current, request, new; +ArgList in_args; +Cardinal *in_num_args; +{ + MyDialogWidget w = (MyDialogWidget)new; + Boolean label_change = False; + int i; + + for (i = 0; i < *in_num_args; i++) { + if (strcmp(XtNlabel, in_args[i].name) == 0) label_change = True; + } + + if (label_change) { + TextFieldSetString(w->mydialog.messageW, w->mydialog.label); + } + + return False; +} + +Widget +#if NeedFunctionPrototypes +MyDialogAddButton(Widget mydialog, _Xconst char* name, XtCallbackProc function, + XtPointer param) +#else +MyDialogAddButton(mydialog, name, function, param) +Widget mydialog; +String name; +XtCallbackProc function; +XtPointer param; +#endif +{ +/* + * Correct Constraints are all set in ConstraintInitialize(). + */ + Widget button; + + button = XtVaCreateManagedWidget(name, + commandWidgetClass, mydialog, NULL); + + if (function != NULL) /* don't add NULL callback func. */ + XtAddCallback(button, XtNcallback, function, param); + return(button); +} + + +void +#if NeedFunctionPrototypes +MyDialogSetMessage(Widget w, char *message) +#else +MyDialogSetMessage(w, message) +Widget w; +char *message; +#endif +{ + TextFieldSetString(((MyDialogWidget)w)->mydialog.messageW, message); + return; +} + +void +#if NeedFunctionPrototypes +MyDialogSetValue(Widget w, char *value) +#else +MyDialogSetValue(w, value) +Widget w; +char *value; +#endif +{ + TextFieldSetString(((MyDialogWidget)w)->mydialog.valueW, value); + return; +} + +char * +#if NeedFunctionPrototypes +MyDialogGetValue(Widget w) +#else +MyDialogGetValue(w) +Widget w; +#endif +{ + return(TextFieldGetString(((MyDialogWidget)w)->mydialog.valueW)); +} + +/* + * MyPressReturn + */ +static void +MyPressReturn(w, event, params, num_params) +Widget w; +XEvent *event; +String *params; +Cardinal *num_params; +{ + MyDialogWidget dw; + + dw = (MyDialogWidget)XtParent(w); + XtCallCallbackList(XtParent(w), dw->mydialog.callbacks, (XtPointer) NULL); + + return; +} diff --git a/mxw/MyDialog.h b/mxw/MyDialog.h new file mode 100644 index 0000000..0fbfdf7 --- /dev/null +++ b/mxw/MyDialog.h @@ -0,0 +1,119 @@ +/* Modified by John */ + +/*********************************************************** + +Copyright (c) 1987, 1988, 1994 X Consortium + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of the X Consortium shall not be +used in advertising or otherwise to promote the sale, use or other dealings +in this Software without prior written authorization from the X Consortium. + + +Copyright 1987, 1988 by Digital Equipment Corporation, Maynard, Massachusetts. + + All Rights Reserved + +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose and without fee is hereby granted, +provided that the above copyright notice appear in all copies and that +both that copyright notice and this permission notice appear in +supporting documentation, and that the name of Digital not be +used in advertising or publicity pertaining to distribution of the +software without specific, written prior permission. + +DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING +ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL +DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR +ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS +SOFTWARE. + +******************************************************************/ + +#ifndef _MyDialog_h +#define _MyDialog_h + +#include + +/*********************************************************************** + * + * MyDialog Widget + * + ***********************************************************************/ + +/* Parameters: + + Name Class RepType Default Value + ---- ----- ------- ------------- + background Background Pixel XtDefaultBackground + border BorderColor Pixel XtDefaultForeground + borderWidth BorderWidth Dimension 1 + destroyCallback Callback Pointer NULL + height Height Dimension computed at create + icon Icon Pixmap 0 + label Label String NULL + mappedWhenManaged MappedWhenManaged Boolean True + sensitive Sensitive Boolean True + value Value String NULL + width Width Dimension computed at create + x Position Position 0 + y Position Position 0 + +*/ + +#define XtCIcon "Icon" +#define XtNicon "icon" + +typedef struct _MyDialogClassRec *MyDialogWidgetClass; +typedef struct _MyDialogRec *MyDialogWidget; + +extern WidgetClass mydialogWidgetClass; + +extern Widget MyDialogAddButton( +#if NeedFunctionPrototypes + Widget /* mydialog */, + _Xconst char* /* name */, + XtCallbackProc /* function */, + XtPointer /* client_data */ +#endif +); + +extern char *MyDialogGetValue( +#if NeedFunctionPrototypes + Widget /* w */ +#endif +); + +extern void MyDialogSetValue( +#if NeedFunctionPrototypes + Widget /* w */, + char * +#endif +); + +extern void MyDialogSetMessage( +#if NeedFunctionPrototypes + Widget /* w */, + char * +#endif +); + +#endif /* _MyDialog_h */ diff --git a/mxw/MyDialogP.h b/mxw/MyDialogP.h new file mode 100644 index 0000000..d9a64d7 --- /dev/null +++ b/mxw/MyDialogP.h @@ -0,0 +1,94 @@ +/* Modified by john */ + +/*********************************************************** + +Copyright (c) 1987, 1988 X Consortium + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN +AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN +CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of the X Consortium shall not be +used in advertising or otherwise to promote the sale, use or other dealings +in this Software without prior written authorization from the X Consortium. + + +Copyright 1987, 1988 by Digital Equipment Corporation, Maynard, Massachusetts. + + All Rights Reserved + +Permission to use, copy, modify, and distribute this software and its +documentation for any purpose and without fee is hereby granted, +provided that the above copyright notice appear in all copies and that +both that copyright notice and this permission notice appear in +supporting documentation, and that the name of Digital not be +used in advertising or publicity pertaining to distribution of the +software without specific, written prior permission. + +DIGITAL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, INCLUDING +ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS, IN NO EVENT SHALL +DIGITAL BE LIABLE FOR ANY SPECIAL, INDIRECT OR CONSEQUENTIAL DAMAGES OR +ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, +WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, +ARISING OUT OF OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS +SOFTWARE. + +******************************************************************/ + +/* Private definitions for MyDialog widget */ + +#ifndef _MyDialogP_h +#define _MyDialogP_h + +#include "MyDialog.h" +#include + +typedef struct {int empty;} MyDialogClassPart; + +typedef struct _MyDialogClassRec { + CoreClassPart core_class; + CompositeClassPart composite_class; + ConstraintClassPart constraint_class; + FormClassPart form_class; + MyDialogClassPart mydialog_class; +} MyDialogClassRec; + +extern MyDialogClassRec mydialogClassRec; + +typedef struct _MyDialogPart { + String label; /* initial message string */ + XtCallbackList callbacks; /* callbacks */ + /* private data */ + Widget valueW; /* user response text field */ + Widget messageW; /* message text field */ +} MyDialogPart; + +typedef struct _MyDialogRec { + CorePart core; + CompositePart composite; + ConstraintPart constraint; + FormPart form; + MyDialogPart mydialog; +} MyDialogRec; + +typedef struct {int empty;} MyDialogConstraintsPart; + +typedef struct _MyDialogConstraintsRec { + FormConstraintsPart form; + MyDialogConstraintsPart mydialog; +} MyDialogConstraintsRec, *MyDialogConstraints; + +#endif /* _MyDialogP_h */ diff --git a/mxw/TextField.c b/mxw/TextField.c new file mode 100644 index 0000000..fdc0c36 --- /dev/null +++ b/mxw/TextField.c @@ -0,0 +1,1633 @@ +/*----------------------------------------------------------------------------- + * TextField A single line text entry widget + * + * Copyright (c) 1995 Robert W. McMullen + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * + * Author: Rob McMullen + * http://www.ae.utexas.edu/~rwmcm + */ + +#define _TextField_ + +#include +#include +#include +#include + +#include + +#ifdef HAVE_STDLIB_H +#include +#endif + +#include "TextFieldP.h" + +#define offset(field) XtOffsetOf(TextFieldRec, text.field) +static XtResource resources[] = +{ + {XtNallowSelection, XtCBoolean, XtRBoolean, sizeof(Boolean), + offset(AllowSelection), XtRString, "True"}, + {XtNdisplayCaret, XtCBoolean, XtRBoolean, sizeof(Boolean), + offset(DisplayCursor), XtRString, "True"}, + {XtNecho, XtCBoolean, XtRBoolean, sizeof(Boolean), + offset(Echo), XtRString, "True"}, + {XtNeditable, XtCBoolean, XtRBoolean, sizeof(Boolean), + offset(Editable), XtRString, "True"}, + {XtNfont, XtCFont, XtRFontStruct, sizeof(XFontStruct *), + offset(font), XtRString, XtDefaultFont}, + {XtNforeground, XtCForeground, XtRPixel, sizeof(Pixel), + offset(foreground_pixel), XtRString, XtDefaultForeground}, + {XtNinsertPosition, XtCInsertPosition, XtRInt, sizeof(int), + offset(CursorPos), XtRString, "0"}, + {XtNlength, XtCLength, XtRInt, sizeof(int), + offset(TextMaxLen), XtRString, "0"}, + {XtNmargin, XtCMargin, XtRDimension, sizeof(Dimension), + offset(Margin), XtRString, "3"}, + {XtNpendingDelete, XtCBoolean, XtRBoolean, sizeof(Boolean), + offset(PendingDelete), XtRString, "True"}, + {XtNstring, XtCString, XtRString, sizeof(char *), + offset(DefaultString), XtRString, NULL}, + {XtNactivateCallback, XtCCallback, XtRCallback, sizeof(XtPointer), + offset(ActivateCallback), XtRCallback, NULL}, +}; + +#undef offset + +static void Initialize(); +static void Destroy(); +static void Redisplay(); +static void Resize(); +static Boolean SetValues(); +static void Draw(), DrawInsert(), MassiveChangeDraw(), DrawTextReposition(), + ClearHighlight(), DrawHighlight(), DrawCursor(), EraseCursor(); +static Boolean PositionCursor(), MassiveCursorAdjust(); +static void Nothing(), Activate(), InsertChar(), ForwardChar(), + ForwardToEnd(), BackwardChar(), BackwardToStart(), DeleteNext(), + DeleteToEnd(), DeletePrev(), DeleteToStart(), DeleteHighlighted(), + TransposeChars(), SelectStart(), ExtendStart(), ExtendAdjust(), + ExtendEnd(), InsertSelection(); + +static char defaultTranslations[] = +"Right: forward-char()\n\ + KP_Right: forward-char()\n\ + Ctrlf: forward-char()\n\ + Ctrle: forward-to-end()\n\ + Left: backward-char()\n\ + KP_Left: backward-char()\n\ + Ctrlb: backward-char()\n\ + Ctrla: backward-to-start()\n\ + Ctrld: delete-next-char()\n\ + Ctrlk: delete-to-end()\n\ + Delete: delete-previous-char()\n\ + BackSpace: delete-previous-char()\n\ + Ctrlh: delete-previous-char()\n\ + Ctrlu: delete-to-start()\n\ + Ctrlw: delete-highlighted()\n\ + Ctrlt: transpose-chars()\n\ + Return: activate()\n\ + : insert-char()\n\ + Shift: extend-start()\n\ + : select-start()\n\ + : extend-adjust()\n\ + : extend-end()\n\ + : insert-selection()\n\ + : extend-start()\n\ + : extend-adjust()\n\ + : extend-end()\n\ + : enter-window()\n\ + : leave-window()\n\ + : focus-in()\n\ + : focus-out()"; + +static XtActionsRec actions[] = +{ + {"insert-char", InsertChar}, + {"forward-char", ForwardChar}, + {"forward-to-end", ForwardToEnd}, + {"backward-char", BackwardChar}, + {"backward-to-start", BackwardToStart}, + {"delete-next-char", DeleteNext}, + {"delete-to-end", DeleteToEnd}, + {"delete-previous-char", DeletePrev}, + {"delete-to-start", DeleteToStart}, + {"delete-highlighted", DeleteHighlighted}, + {"transpose-chars", TransposeChars}, + {"activate", Activate}, + {"select-start", SelectStart}, + {"extend-start", ExtendStart}, + {"extend-adjust", ExtendAdjust}, + {"extend-end", ExtendEnd}, + {"insert-selection", InsertSelection}, + {"enter-window", Nothing}, + {"leave-window", Nothing}, + {"focus-in", Nothing}, + {"focus-out", Nothing}, +}; + +TextFieldClassRec textfieldClassRec = +{ + { + /* core_class fields */ + /* superclass */ (WidgetClass) & widgetClassRec, + /* class_name */ "TextField", + /* widget_size */ sizeof(TextFieldRec), + /* class_initialize */ NULL, + /* class_part_initialize */ NULL, + /* class_inited */ False, + /* initialize */ Initialize, + /* initialize_hook */ NULL, + /* realize */ XtInheritRealize, + /* actions */ actions, + /* num_actions */ XtNumber(actions), + /* resources */ resources, + /* num_resources */ XtNumber(resources), + /* xrm_class */ NULLQUARK, + /* compress_motion */ True, + /* compress_exposure */ XtExposeCompressMultiple, + /* compress_enterleave */ True, + /* visible_interest */ True, + /* destroy */ Destroy, + /* resize */ Resize, + /* expose */ Redisplay, + /* set_values */ SetValues, + /* set_values_hook */ NULL, + /* set_values_almost */ XtInheritSetValuesAlmost, + /* get_values_hook */ NULL, + /* accept_focus */ NULL, + /* version */ XtVersion, + /* callback_private */ NULL, + /* tm_table */ defaultTranslations, + /* query_geometry */ XtInheritQueryGeometry, + /* display_accelerator */ XtInheritDisplayAccelerator, + /* extension */ NULL + }, + { + 0 /* some stupid compilers barf on empty structures */ + } +}; + +WidgetClass textfieldWidgetClass = (WidgetClass) & textfieldClassRec; + +/* Convenience macros */ +#define TopMargin(w) (int)(w->text.Margin - 1) +#define BottomMargin(w) (int)(w->text.Margin) + +/* Font functions */ +#define FontHeight(f) (int)(f->max_bounds.ascent + f->max_bounds.descent) +#define FontDescent(f) (int)(f->max_bounds.descent) +#define FontAscent(f) (int)(f->max_bounds.ascent) +#define FontTextWidth(f,c,l) (int)XTextWidth(f, c, l) + +static void +InitializeGC(TextFieldWidget w) +{ + static char dots[] = + {2, 1, 1}; + XGCValues values; + XtGCMask mask; + + values.line_style = LineSolid; + values.line_width = 0; + values.fill_style = FillSolid; + values.font = w->text.font->fid; + values.background = w->core.background_pixel; + values.foreground = w->text.foreground_pixel; + mask = GCLineStyle | GCLineWidth | GCFillStyle | GCForeground | GCBackground | GCFont; + w->text.drawGC = XtGetGC((Widget) w, mask, &values); + + values.foreground = w->core.background_pixel; + values.background = w->text.foreground_pixel; + w->text.highlightGC = XtGetGC((Widget) w, mask, &values); + + values.line_style = LineSolid; + values.line_width = 0; + values.background = w->core.background_pixel; + values.foreground = w->text.foreground_pixel; + mask = GCLineStyle | GCLineWidth | GCForeground | GCBackground; + w->text.cursorGC = XtGetGC((Widget) w, mask, &values); + + values.foreground = w->core.background_pixel; + values.background = w->text.foreground_pixel; + w->text.eraseGC = XtGetGC((Widget) w, mask, &values); + + values.line_style = LineOnOffDash; + values.background = w->core.background_pixel; + values.foreground = w->text.foreground_pixel; + w->text.dashGC = XtGetGC((Widget) w, mask, &values); + XSetDashes(XtDisplay(w), w->text.dashGC, 0, &dots[1], + (int) dots[0]); + + w->text.YOffset = TopMargin(w) + FontAscent(w->text.font); +} + +static void +ClipGC(TextFieldWidget w) +{ + XRectangle clip; + + clip.x = 0; + clip.y = w->text.YOffset - FontAscent(w->text.font) + 1; + clip.width = w->text.ViewWidth + 1; + clip.height = FontHeight(w->text.font); + XSetClipRectangles(XtDisplay((Widget) w), w->text.drawGC, + w->text.Margin, 0, &clip, 1, Unsorted); + XSetClipRectangles(XtDisplay((Widget) w), w->text.highlightGC, + w->text.Margin, 0, &clip, 1, Unsorted); +} + +static void +SetString(TextFieldWidget w, char *s) +{ + int len; + + if (s) { + len = strlen(s); + if (len > w->text.TextAlloc) { + w->text.TextAlloc += len; + w->text.Text = XtRealloc(w->text.Text, w->text.TextAlloc); + } + strcpy(w->text.Text, s); + w->text.TextLen = len; + w->text.TextWidth = w->text.OldTextWidth = + FontTextWidth(w->text.font, w->text.Text, + w->text.TextLen); + if ((w->text.TextMaxLen > 0) && (w->text.TextLen > w->text.TextMaxLen)) + w->text.TextMaxLen = w->text.TextLen; + } + w->text.DefaultString = w->text.Text; +} + +static void +Initialize(Widget treq, Widget tnew, ArgList args, Cardinal * num) +{ + TextFieldWidget new; + int height; + + new = (TextFieldWidget) tnew; + + new->text.timer_id = (XtIntervalId) 0; + new->text.multi_click_time = XtGetMultiClickTime(XtDisplay((Widget) new)); + new->text.highlight_time = new->text.multi_click_time / 2; + + if (new->text.TextMaxLen > 0) { + new->text.TextAlloc = new->text.TextMaxLen + 1; + } + else { + new->text.TextAlloc = TEXTFIELD_ALLOC_SIZE; + } + new->text.Text = (char *) XtMalloc(new->text.TextAlloc); + new->text.TextLen = 0; + new->text.SelectionText = NULL; + new->text.TextWidth = new->text.OldTextWidth = 0; + if (new->text.DefaultString) + SetString(new, new->text.DefaultString); + + if (new->text.CursorPos > 0) { + if (new->text.CursorPos > new->text.TextLen) { + new->text.CursorPos = new->text.TextLen; + } + } + else { + new->text.CursorPos = 0; + } + new->text.OldCursorX = -1; + new->text.HighlightStart = new->text.HighlightEnd = -1; + new->text.OldHighlightStart = new->text.OldHighlightEnd = -1; + + height = FontHeight(new->text.font); + if (new->core.height == 0) + new->core.height = (Dimension) height + TopMargin(new) + BottomMargin(new); + + if (new->core.width == 0) { + new->text.ViewWidth = 200; + new->core.width = new->text.ViewWidth + 2 * new->text.Margin; + } + else { + int width; + + width = (int) new->core.width - 2 * new->text.Margin; + if (width < 0) + new->text.ViewWidth = new->core.width; + else + new->text.ViewWidth = width; + } + + new->text.XOffset = new->text.OldXOffset = 0; + + InitializeGC(new); + + ClipGC(new); +} + +static void +Destroy(TextFieldWidget w) +{ + XtReleaseGC((Widget) w, w->text.drawGC); + XtReleaseGC((Widget) w, w->text.highlightGC); + if (w->text.SelectionText) + XtFree(w->text.SelectionText); + XtFree(w->text.Text); +} + +static void +Redisplay(Widget aw, XExposeEvent * event, Region region) +{ + TextFieldWidget w = (TextFieldWidget) aw; + + if (!XtIsRealized(aw)) + return; + + Draw(w); +} + +static Boolean +SetValues(Widget current, Widget request, Widget reply, + ArgList args, Cardinal * nargs) +{ + TextFieldWidget w = (TextFieldWidget) current; + TextFieldWidget new = (TextFieldWidget) reply; + Boolean redraw = False; + + if ((w->text.foreground_pixel != new->text.foreground_pixel) || + (w->core.background_pixel != new->core.background_pixel) || + (w->text.font != new->text.font)) { + XtReleaseGC((Widget) w, w->text.drawGC); + XtReleaseGC((Widget) w, w->text.highlightGC); + XtReleaseGC((Widget) w, w->text.cursorGC); + XtReleaseGC((Widget) w, w->text.eraseGC); + XtReleaseGC((Widget) w, w->text.dashGC); + InitializeGC(new); + redraw = True; + } + + if ((w->text.CursorPos != new->text.CursorPos) || + (w->text.DisplayCursor != new->text.DisplayCursor)) { + redraw = True; + } + if (w->text.DefaultString != new->text.DefaultString) { + redraw = True; + SetString(new, new->text.DefaultString); + new->text.HighlightStart = new->text.HighlightEnd = -1; + new->text.CursorPos = new->text.TextLen; +#ifdef DEBUG_TF + printf("SetValues: %s\n", new->text.DefaultString); +#endif + } + + return redraw; +} + + +static void +Resize(Widget aw) +{ + TextFieldWidget w = (TextFieldWidget) aw; + int width, height; + + if (!XtIsRealized(aw)) + return; + + width = w->core.width - 2 * w->text.Margin; + if (width < 0) + w->text.ViewWidth = w->core.width; + else + w->text.ViewWidth = width; + + + height = (((int) w->core.height - FontHeight(w->text.font)) / 2) + + FontAscent(w->text.font); + w->text.YOffset = height; + + ClipGC(w); + + MassiveChangeDraw(w); +} + +static void +TextDelete(TextFieldWidget w, int start, int len) +{ + int i; + + if (len > 0) { + for (i = start + len; i < w->text.TextLen; i++) + w->text.Text[i - len] = w->text.Text[i]; + w->text.TextLen -= len; + w->text.TextWidth = FontTextWidth(w->text.font, w->text.Text, + w->text.TextLen); + w->text.Text[w->text.TextLen] = 0; + } +} + +static void +TextDeleteHighlighted(TextFieldWidget w) +{ + if (w->text.HighlightStart >= 0) { + TextDelete(w, w->text.HighlightStart, + w->text.HighlightEnd - w->text.HighlightStart); + w->text.CursorPos = w->text.HighlightStart; + w->text.HighlightStart = w->text.HighlightEnd = -1; + } +} + + +/* returns value indicating if the text can be redrawn using the fast + * method */ +static Boolean +TextInsert(TextFieldWidget w, char *buf, int len) +{ + int i; + Boolean regular_copy, fast_insert; + + fast_insert = True; + if (len > 0) { + if (w->text.HighlightStart >= 0) { + fast_insert = False; + if (w->text.PendingDelete) + TextDeleteHighlighted(w); + else + ClearHighlight(w); + } + + regular_copy = True; + if (w->text.TextMaxLen > 0) { + if (w->text.TextLen + len > w->text.TextMaxLen) + regular_copy = False; + } + else if (w->text.TextLen + len > w->text.TextAlloc) { + i = TEXTFIELD_ALLOC_SIZE; + if (i < len) + i = len; + w->text.TextAlloc += i + 1; + w->text.Text = XtRealloc(w->text.Text, w->text.TextAlloc); +#ifdef DEBUG_TF + printf("TextInsert: Alloced new space: %d bytes\n", w->text.TextAlloc); +#endif + } + if (regular_copy) { + for (i = w->text.TextLen - 1; i >= w->text.CursorPos; i--) + w->text.Text[i + len] = w->text.Text[i]; + strncpy(&w->text.Text[w->text.CursorPos], buf, len); + w->text.FastInsertCursorStart = w->text.CursorPos; + w->text.FastInsertTextLen = len; + w->text.TextLen += len; + w->text.CursorPos += len; + } + else { + int i1; + + for (i = w->text.TextLen - 1; i >= w->text.CursorPos; i--) + if (i + len < w->text.TextMaxLen) + w->text.Text[i + len] = w->text.Text[i]; + w->text.TextLen += len; + if (w->text.TextLen > w->text.TextMaxLen) + w->text.TextLen = w->text.TextMaxLen; + i1 = w->text.CursorPos; + for (i = 0; i < len; i++) { + if (i1 < w->text.TextMaxLen) + w->text.Text[i1] = *buf++; + else + break; + i1++; + } + w->text.FastInsertCursorStart = w->text.CursorPos; + w->text.FastInsertTextLen = i1 - w->text.CursorPos; + w->text.CursorPos = i1; + } + w->text.TextWidth = FontTextWidth(w->text.font, + w->text.Text, w->text.TextLen); + w->text.Text[w->text.TextLen] = 0; + } + return fast_insert; +} + +static int +TextPixelToPos(TextFieldWidget w, int x) +{ + int i, tot, cur, pos; + + pos = 0; + + x -= (int) w->text.Margin + w->text.XOffset; + +/* check if the cursor is before the 1st character */ + if (x <= 0) { + pos = 0; + } + +/* OK, how 'bout after the last character */ + else if (x > FontTextWidth(w->text.font, w->text.Text, w->text.TextLen)) { + pos = w->text.TextLen; + } + +/* must be in between somewhere... */ + else { + tot = 0; + pos = -1; + for (i = 0; i < w->text.TextLen; i++) { + cur = FontTextWidth(w->text.font, &w->text.Text[i], 1); + if (x < tot + (cur / 2)) { + pos = i; + break; + } + tot += cur; + } + if (pos < 0) + pos = w->text.TextLen; + } + return pos; +} + +/* + * TextField Widget Action procedures + */ + +/* ARGSUSED */ +static void +Nothing(Widget aw, XEvent * event, String * params, Cardinal * num_params) +{ +} + +/* ARGSUSED */ +static void +Activate(Widget aw, XEvent * event, String * params, Cardinal * num_params) +{ + TextFieldWidget w = (TextFieldWidget) aw; + TextFieldReturnStruct ret; + + ret.reason = 0; + ret.event = event; + ret.string = w->text.Text; + + if (XtNactivateCallback) + XtCallCallbacks(aw, XtNactivateCallback, &ret); +} + +/* ARGSUSED */ +static void +ForwardChar(Widget aw, XEvent * event, String * params, Cardinal * num_params) +{ + TextFieldWidget w = (TextFieldWidget) aw; + + if (!w->text.Editable) + return; + + ClearHighlight(w); + if (w->text.CursorPos < w->text.TextLen) { + w->text.CursorPos++; + EraseCursor(w); + if (PositionCursor(w)) + DrawTextReposition(w); + DrawCursor(w); + } +} + +/* ARGSUSED */ +static void +ForwardToEnd(Widget aw, XEvent * event, String * params, Cardinal * num_params) +{ + TextFieldWidget w = (TextFieldWidget) aw; + + if (!w->text.Editable) + return; + + ClearHighlight(w); + if (w->text.CursorPos < w->text.TextLen) { + w->text.CursorPos = w->text.TextLen; + EraseCursor(w); + if (PositionCursor(w)) + DrawTextReposition(w); + DrawCursor(w); + } +} + +/* ARGSUSED */ +static void +BackwardChar(Widget aw, XEvent * event, String * params, Cardinal * num_params) +{ + TextFieldWidget w = (TextFieldWidget) aw; + + if (!w->text.Editable) + return; + + ClearHighlight(w); + if (w->text.CursorPos > 0) { + w->text.CursorPos--; + EraseCursor(w); + if (PositionCursor(w)) + DrawTextReposition(w); + DrawCursor(w); + } +} + +/* ARGSUSED */ +static void +BackwardToStart(Widget aw, XEvent * event, String * params, Cardinal * num_params) +{ + TextFieldWidget w = (TextFieldWidget) aw; + + if (!w->text.Editable) + return; + + ClearHighlight(w); + if (w->text.CursorPos > 0) { + w->text.CursorPos=0; + EraseCursor(w); + if (PositionCursor(w)) + DrawTextReposition(w); + DrawCursor(w); + } +} + +/* ARGSUSED */ +static void +InsertChar(Widget aw, XEvent * event, String * params, Cardinal * num_params) +{ + TextFieldWidget w = (TextFieldWidget) aw; + int len, i, j; + +#define INSERTCHARBUFSIZ 32 + char buf[INSERTCHARBUFSIZ]; + + if (!w->text.Editable) + return; + + len = XLookupString((XKeyEvent *) event, buf, BUFSIZ, NULL, NULL); + /* Throw away characters with values < Space (32) */ + for (i = 0; i < len; i++) { + if (buf[i] < 32) { + for (j = 0; j < len && j < (BUFSIZ - 1); j++) { + buf[j] = buf[j+1]; + } + buf[j] = '\0'; + len-=1; + } + } + if (len > 0) { + EraseCursor(w); + if (TextInsert(w, buf, len)) + DrawInsert(w); + else + Draw(w); + } +} + +/* ARGSUSED */ +static void +DeleteNext(Widget aw, XEvent * event, String * params, Cardinal * num_params) +{ + TextFieldWidget w = (TextFieldWidget) aw; + + if (!w->text.Editable) + return; + + if (w->text.CursorPos < w->text.TextLen) { + ClearHighlight(w); + TextDelete(w, w->text.CursorPos, 1); + Draw(w); + } +} + +/* ARGSUSED */ +static void +DeleteToEnd(Widget aw, XEvent * event, String * params, Cardinal * num_params) +{ + TextFieldWidget w = (TextFieldWidget) aw; + + if (!w->text.Editable) + return; + + if (w->text.CursorPos < w->text.TextLen) { + ClearHighlight(w); + TextDelete(w, w->text.CursorPos, w->text.TextLen-w->text.CursorPos); + Draw(w); + } +} + +/* ARGSUSED */ +static void +DeletePrev(Widget aw, XEvent * event, String * params, Cardinal * num_params) +{ + TextFieldWidget w = (TextFieldWidget) aw; + + if (!w->text.Editable) + return; + + if (w->text.CursorPos > 0) { + ClearHighlight(w); + TextDelete(w, w->text.CursorPos - 1, 1); + w->text.CursorPos--; + Draw(w); + } +} + +/* ARGSUSED */ +static void +DeleteToStart(Widget aw, XEvent * event, String * params, Cardinal * num_params) +{ + TextFieldWidget w = (TextFieldWidget) aw; + + if (!w->text.Editable) + return; + + if (w->text.CursorPos > 0) { + ClearHighlight(w); + TextDelete(w, 0, w->text.CursorPos); + w->text.CursorPos=0; + Draw(w); + } +} + +/* ARGSUSED */ +static void +DeleteHighlighted(Widget aw, XEvent * event, String * params, Cardinal * num_params) +{ + TextFieldWidget w = (TextFieldWidget) aw; + + if (!w->text.Editable) + return; + + if (w->text.HighlightStart >= 0 && w->text.PendingDelete) { + TextDeleteHighlighted(w); + MassiveChangeDraw(w); + } +} + +/* ARGSUSED */ +static void +TransposeChars(Widget aw, XEvent * event, String * params, Cardinal * num_params) +{ + TextFieldWidget w = (TextFieldWidget) aw; + char c; + + if (!w->text.Editable) + return; + + ClearHighlight(w); + if (w->text.CursorPos > 0 && w->text.CursorPos < w->text.TextLen) { + c = w->text.Text[w->text.CursorPos -1]; + TextDelete(w, w->text.CursorPos - 1, 1); + TextInsert(w, &c, 1); + Draw(w); + } +} + +/* ARGSUSED */ +static void +SelectStart(Widget aw, XEvent * event, String * params, Cardinal * num_params) +{ + TextFieldWidget w = (TextFieldWidget) aw; + + if (!w->text.AllowSelection) + return; + + w->text.CursorPos = TextPixelToPos(w, event->xbutton.x); + w->text.HighlightPivotStart = + w->text.HighlightPivotEnd = w->text.CursorPos; + + if (w->text.HighlightStart >= 0) { + ClearHighlight(w); + } + else { + EraseCursor(w); + DrawCursor(w); + } +} + +/* ARGSUSED */ +static void +ExtendStart(Widget aw, XEvent * event, String * params, Cardinal * num_params) +{ + TextFieldWidget w = (TextFieldWidget) aw; + int pos; + + if (!w->text.AllowSelection) + return; + + pos = TextPixelToPos(w, event->xbutton.x); + + EraseCursor(w); + if (w->text.HighlightStart < 0) { + w->text.HighlightStart = + w->text.HighlightEnd = + w->text.HighlightPivotStart = + w->text.HighlightPivotEnd = w->text.CursorPos; + } + else { + w->text.HighlightPivotStart = w->text.HighlightStart; + w->text.HighlightPivotEnd = w->text.HighlightEnd; + } + if (pos < w->text.HighlightStart) { + w->text.HighlightStart = pos; + } + else { + w->text.HighlightEnd = pos; + } + w->text.CursorPos = pos; +#ifdef DEBUG_TF + printf("ExtendStart: %d - %d\n", w->text.HighlightStart, + w->text.HighlightEnd); +#endif + DrawHighlight(w); + DrawCursor(w); +} + +static void +ExtendHighlight(TextFieldWidget w) +{ + int x, pos; + + if (!w->text.AllowSelection) + return; + + x = w->text.timer_x; + pos = TextPixelToPos(w, x); + + if (x < (int) w->text.Margin) { + pos = TextPixelToPos(w, (int) 0); + if (pos > 0) + pos--; + else if (pos == w->text.CursorPos) + return; + } + else if (x > (int) (w->text.Margin + w->text.ViewWidth)) { + pos = TextPixelToPos(w, (int) (w->text.Margin + w->text.ViewWidth)); + if (pos < w->text.TextLen) + pos++; + else if (pos == w->text.CursorPos) + return; + } + if (pos == w->text.CursorPos) + return; + + EraseCursor(w); + if (pos <= w->text.HighlightPivotStart) { + w->text.HighlightStart = pos; + w->text.HighlightEnd = w->text.HighlightPivotEnd; + } + else { + w->text.HighlightStart = w->text.HighlightPivotStart; + w->text.HighlightEnd = pos; + } + w->text.CursorPos = pos; +#ifdef DEBUG_TF + printf("Highlighting: x=%d pos=%d %d - %d\n", x, pos, w->text.HighlightStart, + w->text.HighlightEnd); +#endif + if (PositionCursor(w)) + DrawTextReposition(w); + DrawHighlight(w); + DrawCursor(w); +} + +static void +ExtendTimer(XtPointer client_data, XtIntervalId * idp) +{ + TextFieldWidget w = (TextFieldWidget) client_data; + + ExtendHighlight(w); + w->text.timer_id = XtAppAddTimeOut( + XtWidgetToApplicationContext((Widget) w), + (unsigned long) w->text.highlight_time, + ExtendTimer, + (XtPointer) w); + +} + +/* ARGSUSED */ +static void +ExtendAdjust(Widget aw, XEvent * event, String * params, Cardinal * num_params) +{ + TextFieldWidget w = (TextFieldWidget) aw; + + if (!w->text.AllowSelection) + return; + + w->text.timer_x = event->xbutton.x; + + if (event->xbutton.x < w->text.Margin || event->xbutton.x > w->text.Margin + w->text.ViewWidth) { + if (w->text.timer_id) + ExtendHighlight(w); + else + ExtendTimer((XtPointer) w, (XtIntervalId) 0); + } + else { + if (w->text.timer_id) { + XtRemoveTimeOut(w->text.timer_id); + w->text.timer_id = (XtIntervalId) 0; + } + ExtendHighlight(w); + } +} + +/* ARGSUSED */ +static Boolean +ConvertSelection(Widget aw, Atom * selection, Atom * target, Atom * type, + XtPointer * value, unsigned long *length, int *format) +{ + TextFieldWidget w = (TextFieldWidget) aw; + XSelectionRequestEvent *req = XtGetSelectionRequest(aw, *selection, NULL); + + if (*target == XA_TARGETS(XtDisplay(aw))) { + Atom *targetP, *std_targets; + unsigned long std_length; + + XmuConvertStandardSelection(aw, req->time, selection, + target, type, &std_targets, + &std_length, format); + + *value = XtMalloc((unsigned) sizeof(Atom) * (std_length + 1)); + targetP = *(Atom **) value; + *length = std_length + 1; + *targetP++ = XA_STRING; + memmove((char *) targetP, (char *) std_targets, sizeof(Atom) * std_length); + XtFree((char *) std_targets); + *type = XA_ATOM; + *format = sizeof(Atom) * 8; + return True; + } + else if (*target == XA_STRING) { + *length = (long) w->text.SelectionLen; + *value = w->text.SelectionText; + *type = XA_STRING; + *format = 8; + return True; + } + return False; +} + +/* ARGSUSED */ +static void +LoseSelection(Widget aw, Atom * selection) +{ + TextFieldWidget w = (TextFieldWidget) aw; + + ClearHighlight(w); +} + +/* ARGSUSED */ +static void +ExtendEnd(Widget aw, XEvent * event, String * params, Cardinal * num_params) +{ + TextFieldWidget w = (TextFieldWidget) aw; + int len; + + if (!w->text.AllowSelection) + return; + + if (w->text.timer_id) { + XtRemoveTimeOut(w->text.timer_id); + w->text.timer_id = (XtIntervalId) 0; + } + len = w->text.HighlightEnd - w->text.HighlightStart; + if (len > 0) { + w->text.SelectionLen = len; + if (w->text.SelectionText) + XtFree(w->text.SelectionText); + w->text.SelectionText = XtMalloc(len); + strncpy(w->text.SelectionText, &w->text.Text[w->text.HighlightStart], len); + + XtOwnSelection(aw, XA_PRIMARY, event->xbutton.time, + ConvertSelection, LoseSelection, NULL); + XChangeProperty(XtDisplay(aw), DefaultRootWindow(XtDisplay(aw)), + XA_CUT_BUFFER0, XA_STRING, 8, PropModeReplace, + (unsigned char *) w->text.SelectionText, len); + + } +} + +/* ARGSUSED */ +static void +RequestSelection(Widget aw, XtPointer client, Atom * selection, Atom * type, + XtPointer value, unsigned long *length, int *format) +{ + TextFieldWidget w = (TextFieldWidget) aw; + + if ((value == NULL) || (*length == 0)) { +#ifdef DEBUG_TF + printf("RequestSelection: no selection\n"); +#endif + } + else { + int savex; + + ClearHighlight(w); + savex = w->text.OldCursorX; + w->text.CursorPos = (int) client; +#ifdef DEBUG_TF + printf("RequestSelection: inserting %s length=%d at pos: %d\n", + (char *) value, (int) (*length), w->text.CursorPos); +#endif + TextInsert(w, (char *) value, (int) (*length)); + w->text.OldCursorX = savex; + Draw(w); + } +} + +/* ARGSUSED */ +static void +InsertSelection(Widget aw, XEvent * event, String * params, Cardinal * num_params) +{ + TextFieldWidget w = (TextFieldWidget) aw; + int pos; + + if (!w->text.AllowSelection) + return; + + pos = TextPixelToPos(w, event->xbutton.x); +#ifdef DEBUG_TF + printf("InsertSelection: event at pos: %d\n", pos); +#endif + XtGetSelectionValue(aw, XA_PRIMARY, XA_STRING, + RequestSelection, + (XtPointer) pos, event->xbutton.time); +} + + +/* + * TextField private drawing functions + */ + +static Boolean +PositionCursor(TextFieldWidget w) +{ + int x, start, end; + Boolean moved; + + moved = False; + if (w->text.CursorPos < 0) + w->text.CursorPos = 0; + else if (w->text.CursorPos > w->text.TextLen) + w->text.CursorPos = w->text.TextLen; + x = FontTextWidth(w->text.font, w->text.Text, w->text.CursorPos); + start = -w->text.XOffset; + end = start + w->text.ViewWidth; + if (x < start) { + w->text.XOffset = -x; + moved = True; + } + else if (x > end) { + w->text.XOffset = w->text.ViewWidth - x; + moved = True; + } + return moved; +} + +static Boolean +MassiveCursorAdjust(TextFieldWidget w) +{ + int start, end, last; + Boolean moved; + + moved = False; + end = FontTextWidth(w->text.font, w->text.Text, w->text.CursorPos); + if (w->text.HighlightStart >= 0) + start = FontTextWidth(w->text.font, w->text.Text, w->text.HighlightStart); + else + start = end; + + if (end < w->text.ViewWidth) { + if (w->text.XOffset < 0) { + w->text.XOffset = 0; + moved = True; + } + } + else if (start >= w->text.XOffset && end < w->text.XOffset + w->text.ViewWidth) + return moved; + else { + last = FontTextWidth(w->text.font, w->text.Text, w->text.TextLen); + if (start - end > w->text.ViewWidth) { + if (last - end > w->text.ViewWidth) + w->text.XOffset = w->text.ViewWidth - last; + else + w->text.XOffset = w->text.ViewWidth - end; + } + else if (end > w->text.ViewWidth) + w->text.XOffset = w->text.ViewWidth - end; + else + w->text.XOffset = 0; + moved = True; + } + return moved; +} + +/* + * Actually draw a range of text onto the widget + */ +static void +DrawText(TextFieldWidget w, int start, int end, Boolean highlight) +{ + int x; + GC gc; + + if (!w->text.Echo) + return; + + if (w->text.TextLen > 0) { + if (start < 0) + return; + else if (end < start) { + int temp; + + temp = start; + start = end; + end = temp; + } + if (end <= w->text.TextLen) { + x = w->text.Margin + w->text.XOffset + + FontTextWidth(w->text.font, w->text.Text, start); + if (highlight) + gc = w->text.highlightGC; + else + gc = w->text.drawGC; + XDrawImageString(XtDisplay(w), XtWindow(w), gc, + x, w->text.YOffset, + &w->text.Text[start], end - start); + } + } +} + +static void +DrawTextRange(TextFieldWidget w, int start, int end) +{ + if (!w->text.Echo) + return; + + if (w->text.TextLen > 0) { + if (start < 0) + return; + else if (end < start) { + int temp; + + temp = start; + start = end; + end = temp; + } + +/* If there is no highlighting, or the refresh area doesn't cross the */ +/* the highlight borders, just redraw it. */ + if (w->text.HighlightStart < 0 || + start >= w->text.HighlightEnd || + end <= w->text.HighlightStart) { + DrawText(w, start, end, False); + } + +/* OK, the refresh area crosses one or both highlight borders. */ + else { + int clip; + + while (start < end) { + if (start < w->text.HighlightStart) { + if (end <= w->text.HighlightStart) + clip = end; + else + clip = w->text.HighlightStart; + DrawText(w, start, clip, False); + start = clip; + } + else if (start < w->text.HighlightEnd) { + if (end <= w->text.HighlightEnd) + clip = end; + else + clip = w->text.HighlightEnd; + DrawText(w, start, clip, True); + start = clip; + } + else { + DrawText(w, start, end, False); + start = end; + } + } + + + } + } +} + +static void +DrawTextReposition(TextFieldWidget w) +{ + int xsrc, xdest, width, start, end; + + if (!w->text.Echo) + return; + + if (w->text.XOffset < w->text.OldXOffset) { + xsrc = w->text.OldXOffset - w->text.XOffset; + xdest = 0; + width = w->text.ViewWidth - xsrc + 1; + + /* Need to redraw some characters at the end. */ + + end = TextPixelToPos(w, w->text.Margin + w->text.ViewWidth); + start = TextPixelToPos(w, w->text.Margin + w->text.ViewWidth - xsrc); + } + else if (w->text.XOffset > w->text.OldXOffset) { + xsrc = 0; + xdest = w->text.XOffset - w->text.OldXOffset; + width = w->text.ViewWidth - xdest + 1; + + /* Need to redraw some characters at the beginning. */ + + start = TextPixelToPos(w, w->text.Margin); + end = TextPixelToPos(w, w->text.Margin + xdest); + } + else + return; + + if (width > 0) { +#ifdef DEBUG_TF + printf("Reposition: xoff=%d old=%d src=%d dest=%d width=%d refresh %d-%d\n", + w->text.XOffset, w->text.OldXOffset, xsrc, xdest, width, start, end); +#endif + XCopyArea(XtDisplay(w), XtWindow(w), XtWindow(w), + w->text.drawGC, + w->text.Margin + xsrc, 0, + (unsigned int) width, (unsigned int) w->core.height, + w->text.Margin + xdest, 0); + DrawTextRange(w, start, end); + } + w->text.OldXOffset = w->text.XOffset; +} + +static void +DrawTextWithCopyArea(TextFieldWidget w) +{ + int x, insert_width; + int xsrc, xdest, width; + + if (!w->text.Echo) + return; + + x = w->text.XOffset; + insert_width = FontTextWidth(w->text.font, &w->text.Text[w->text.FastInsertCursorStart], w->text.FastInsertTextLen); + if (PositionCursor(w)) { + /* + * if the text is scrolled, then: + * 1. the cursor is at the end + * 2. the copy will move to the left. + */ + xsrc = 0; + width = w->text.OldCursorX + x; + xdest = w->text.ViewWidth - (x + w->text.OldCursorX) - insert_width; + XCopyArea(XtDisplay(w), XtWindow(w), XtWindow(w), + w->text.drawGC, + w->text.Margin + xsrc, 0, + (unsigned int) width, (unsigned int) w->core.height, + w->text.Margin + xdest, 0); +#ifdef DEBUG_TF + printf("DrawInsert: x=%d xsrc=%d xdest=%d width=%d\n", x, xsrc, xdest, width); +#endif + } + else { + + /* + * the text hasn't been scrolled, so: + * 1. the text left of the cursor won't change + * 2. the stuff after the cursor will be moved right. + */ + xsrc = FontTextWidth(w->text.font, w->text.Text, w->text.FastInsertCursorStart) + x; + width = w->text.ViewWidth - xsrc; + xdest = xsrc + insert_width; + XCopyArea(XtDisplay(w), XtWindow(w), XtWindow(w), + w->text.drawGC, + w->text.Margin + xsrc, 0, + (unsigned int) width, (unsigned int) w->core.height, + w->text.Margin + xdest, 0); +#ifdef DEBUG_TF + printf("DrawInsert: x=%d xsrc=%d xdest=%d width=%d\n", x, xsrc, xdest, width); +#endif + } + DrawTextRange(w, w->text.FastInsertCursorStart, + w->text.FastInsertCursorStart + w->text.FastInsertTextLen); + if (w->text.TextMaxLen > 0) { + + /* + * This is pretty much a hack: + * clear everything to end of window if this is a + * fixed length TextField + */ + xsrc = w->text.XOffset + w->text.TextWidth; + width = w->text.ViewWidth - xsrc; + /* printf("DrawInsert: TextWidth=%d Old=%d\n", w->text.TextWidth, w->text.OldTextWidth); */ + XClearArea(XtDisplay(w), XtWindow(w), + w->text.Margin + xsrc, 0, + (unsigned int) width, w->core.height, False); + } + else if (w->text.TextWidth < w->text.OldTextWidth) { + XClearArea(XtDisplay(w), XtWindow(w), + w->text.Margin + w->text.XOffset + w->text.TextWidth, 0, + w->text.OldTextWidth - w->text.TextWidth + 1, + w->core.height, False); + } + w->text.OldTextWidth = w->text.TextWidth; + w->text.OldXOffset = w->text.XOffset; +} + +static void +DrawAllText(TextFieldWidget w) +{ + if (!w->text.Echo) + return; + + DrawTextRange(w, 0, w->text.TextLen); + if (w->text.TextWidth < w->text.OldTextWidth) { + XClearArea(XtDisplay(w), XtWindow(w), + w->text.Margin + w->text.XOffset + w->text.TextWidth, 0, + w->text.OldTextWidth - w->text.TextWidth + 1, + w->core.height, False); + } + w->text.OldTextWidth = w->text.TextWidth; + w->text.OldXOffset = w->text.XOffset; + w->text.OldHighlightStart = w->text.HighlightStart; + w->text.OldHighlightEnd = w->text.HighlightEnd; +} + +/* Draw an I-beam cursor */ +static void +DrawIBeamCursor(TextFieldWidget w, int x, GC gc) +{ + XDrawLine(XtDisplay(w), XtWindow(w), gc, + x, w->text.YOffset - FontAscent(w->text.font) - 1, + x, w->text.YOffset + FontDescent(w->text.font) + 1); + XDrawLine(XtDisplay(w), XtWindow(w), gc, + x - 2, w->text.YOffset - FontAscent(w->text.font) - 1, + x + 2, w->text.YOffset - FontAscent(w->text.font) - 1); + XDrawLine(XtDisplay(w), XtWindow(w), gc, + x - 2, w->text.YOffset + FontDescent(w->text.font) + 1, + x + 2, w->text.YOffset + FontDescent(w->text.font) + 1); +} + +static void +DrawCursor(TextFieldWidget w) +{ + int x; + GC gc; + + if (w->text.DisplayCursor) { + x = FontTextWidth(w->text.font, w->text.Text, w->text.CursorPos); + w->text.OldCursorPos = w->text.CursorPos; + w->text.OldCursorX = x; + x += w->text.Margin + w->text.XOffset; + + gc = w->text.cursorGC; + DrawIBeamCursor(w, x, gc); + } +} + +static void +EraseCursor(TextFieldWidget w) +{ + int x; + + if (w->text.DisplayCursor && w->text.OldCursorX >= 0) { + x = w->text.OldCursorX + w->text.Margin + w->text.XOffset; + DrawIBeamCursor(w, x, w->text.eraseGC); + + /* Little hack to fix up the character that might have been affected by + * erasing the old cursor. + */ + if (w->text.OldCursorPos < w->text.TextLen) + DrawTextRange(w, w->text.OldCursorPos, w->text.OldCursorPos + 1); + } +} + +static void +ClearHighlight(TextFieldWidget w) +{ + if (!w->text.Echo) + return; + + if (w->text.HighlightStart >= 0) { + EraseCursor(w); + DrawText(w, w->text.HighlightStart, w->text.HighlightEnd, False); + DrawCursor(w); + w->text.HighlightStart = w->text.HighlightEnd = -1; + } + w->text.OldHighlightStart = w->text.OldHighlightEnd = -1; +} + +static void +DrawHighlight(TextFieldWidget w) +{ + if (!w->text.Echo) + return; + + if (w->text.OldHighlightStart < 0) { + DrawText(w, w->text.HighlightStart, w->text.HighlightEnd, + True); + } + else { + DrawText(w, w->text.HighlightStart, w->text.OldHighlightStart, + (w->text.HighlightStart < w->text.OldHighlightStart)); + DrawText(w, w->text.HighlightEnd, w->text.OldHighlightEnd, + (w->text.HighlightEnd > w->text.OldHighlightEnd)); + } + w->text.OldHighlightStart = w->text.HighlightStart; + w->text.OldHighlightEnd = w->text.HighlightEnd; +} + +/* + * Special redraw function after a text insertion + */ +static void +DrawInsert(TextFieldWidget w) +{ + /* EraseCursor must be called before this */ + DrawTextWithCopyArea(w); + DrawCursor(w); +} + +/* + * Redraw the entire widget, but don't scroll the window much + */ +static void +Draw(TextFieldWidget w) +{ + EraseCursor(w); + PositionCursor(w); + DrawAllText(w); + DrawCursor(w); +} + +/* + * Like Draw(), but has different rules about scrolling the window to + * place the cursor in a good place + */ +static void +MassiveChangeDraw(TextFieldWidget w) +{ + EraseCursor(w); + MassiveCursorAdjust(w); + DrawAllText(w); + DrawCursor(w); +} + +/* + * Motif-like TextField public functions + * + * Note that this set of functions is only a subset of the functions available + * in the real Motif XmTextField. + */ + +Boolean +TextFieldGetEditable(Widget aw) +{ + TextFieldWidget w = (TextFieldWidget) aw; + + if (!XtIsTextField(aw)) + return 0; + + return w->text.Editable; +} + +int +TextFieldGetInsertionPosition(Widget aw) +{ + TextFieldWidget w = (TextFieldWidget) aw; + + if (!XtIsTextField(aw)) + return 0; + + return w->text.CursorPos; +} + +char * +TextFieldGetString(Widget aw) +{ + TextFieldWidget w = (TextFieldWidget) aw; + char *ret; + + if (!XtIsTextField(aw)) { + ret = XtMalloc(1); + *ret = '\0'; + return ret; + } + + ret = XtMalloc(w->text.TextLen + 1); + strncpy(ret, w->text.Text, w->text.TextLen); + ret[w->text.TextLen] = '\0'; + return ret; +} + +void +TextFieldInsert(Widget aw, int pos, char *str) +{ + TextFieldWidget w = (TextFieldWidget) aw; + int len; + + if (!XtIsTextField(aw)) + return; + + if (str && ((len = strlen(str)) > 0) && pos >= 0 && pos <= w->text.TextLen) { + w->text.HighlightStart = w->text.HighlightEnd = pos; + TextInsert(w, str, len); + MassiveChangeDraw(w); + } +} + +void +TextFieldReplace(Widget aw, int first, int last, char *str) +{ + TextFieldWidget w = (TextFieldWidget) aw; + int len; + + if (!XtIsTextField(aw)) + return; + + if (str) { + len = strlen(str); + if (last > w->text.TextLen) + last = w->text.TextLen; + if (first <= last) { + w->text.HighlightStart = first; + w->text.HighlightEnd = last; + TextDeleteHighlighted(w); + TextInsert(w, str, len); + MassiveChangeDraw(w); + } + } +} + +void +TextFieldSetEditable(Widget aw, Boolean editable) +{ + TextFieldWidget w = (TextFieldWidget) aw; + + if (!XtIsTextField(aw)) + return; + + w->text.Editable = editable; +} + +void +TextFieldSetInsertionPosition(Widget aw, int pos) +{ + TextFieldWidget w = (TextFieldWidget) aw; + + if (!XtIsTextField(aw)) + return; + + if (pos >= 0 && pos <= w->text.TextLen) { + w->text.CursorPos = pos; + MassiveChangeDraw(w); + } +} + +/* ARGSUSED */ +void +TextFieldSetSelection(Widget aw, int start, int end, Time time) +{ + TextFieldWidget w = (TextFieldWidget) aw; + + if (!XtIsTextField(aw)) + return; + + if (end < start) { + int temp; + + temp = start; + start = end; + end = temp; + } + if (start < 0) + start = 0; + if (end > w->text.TextLen) + end = w->text.TextLen; + w->text.HighlightStart = start; + w->text.HighlightEnd = w->text.CursorPos = end; + MassiveChangeDraw(w); +} + +void +TextFieldSetString(Widget aw, char *str) +{ + TextFieldWidget w = (TextFieldWidget) aw; + int len; + + if (!XtIsTextField(aw)) + return; + + if (str) { + len = strlen(str); + w->text.HighlightStart = 0; + w->text.HighlightEnd = w->text.TextLen; + TextDeleteHighlighted(w); + TextInsert(w, str, len); + MassiveChangeDraw(w); + } +} diff --git a/mxw/TextField.h b/mxw/TextField.h new file mode 100644 index 0000000..b039733 --- /dev/null +++ b/mxw/TextField.h @@ -0,0 +1,120 @@ +/*----------------------------------------------------------------------------- + * TextField A single line text entry widget + * + * Copyright (c) 1995 Robert W. McMullen + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * + * Author: Rob McMullen + * http://www.ae.utexas.edu/~rwmcm + */ + +#ifndef _TextField_H +#define _TextField_H + +#include + +#define _TextField_WIDGET_VERSION 1.0 + +#ifndef XtIsTextField +#define XtIsTextField(w) XtIsSubclass((Widget)w, textfieldWidgetClass) +#endif + +/* Athena style resource names */ + +#ifndef XtNecho +#define XtNecho "echo" +#endif +#ifndef XtNpendingDelete +#define XtNpendingDelete "pendingDelete" +#endif +#ifndef XtNlength +#define XtNlength "length" +#endif +#ifndef XtNstring +#define XtNstring "string" +#endif +#ifndef XtNinsertPosition +#define XtNinsertPosition "insertPosition" +#endif +#ifndef XtNdisplayCaret +#define XtNdisplayCaret "displayCaret" +#endif +#ifndef XtNeditable +#define XtNeditable "editable" +#endif +#define XtNmargin "margin" +#define XtNcursorWidth "cursorWidth" +#define XtNallowSelection "allowSelection" +#define XtNactivateCallback "activateCallback" + + +/* Motif style resource names */ + +#ifndef XmNmaxLength +#define XmNmaxLength XtNlength +#endif +#ifndef XmNvalue +#define XmNvalue XtNvalue +#endif +#ifndef XmNcursorPosition +#define XmNcursorPosition XtNinsertPosition +#endif +#ifndef XmNcursorPositionVisible +#define XmNcursorPositionVisible XtNdisplayCaret +#endif +#ifndef XmNeditable +#define XmNeditable XtNeditable +#endif +#ifndef XmNactivateCallback +#define XmNactivateCallback XtNactivateCallback +#endif + + +extern WidgetClass textfieldWidgetClass; + +typedef struct _TextFieldClassRec *TextFieldWidgetClass; +typedef struct _TextFieldRec *TextFieldWidget; + +typedef struct _TextFieldReturnStruct { + int reason; /* Motif compatibility */ + XEvent *event; + char *string; +} TextFieldReturnStruct; + +/* +** Public function declarations +*/ +#if __STDC__ || defined(__cplusplus) +#define P_(s) s +#else +#define P_(s) () +#endif + +/* TextField.c */ +Boolean TextFieldGetEditable P_((Widget aw)); +int TextFieldGetInsertionPosition P_((Widget aw)); +char *TextFieldGetString P_((Widget aw)); +void TextFieldInsert P_((Widget aw, int pos, char *str)); +void TextFieldReplace P_((Widget aw, int first, int last, char *str)); +void TextFieldSetEditable P_((Widget aw, Boolean editable)); +void TextFieldSetInsertionPosition P_((Widget aw, int pos)); +void TextFieldSetSelection P_((Widget aw, int start, int end, Time time)); +void TextFieldSetString P_((Widget aw, char *str)); + +#undef P_ + +#endif /* _TextField_H */ diff --git a/mxw/TextFieldP.h b/mxw/TextFieldP.h new file mode 100644 index 0000000..55981fc --- /dev/null +++ b/mxw/TextFieldP.h @@ -0,0 +1,106 @@ +/*----------------------------------------------------------------------------- + * TextField A single line text entry widget + * + * Copyright (c) 1995 Robert W. McMullen + * + * This library is free software; you can redistribute it and/or + * modify it under the terms of the GNU Library General Public + * License as published by the Free Software Foundation; either + * version 2 of the License, or (at your option) any later version. + * + * This library is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * Library General Public License for more details. + * + * You should have received a copy of the GNU Library General Public + * License along with this library; if not, write to the Free + * Software Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * + * Author: Rob McMullen + * http://www.ae.utexas.edu/~rwmcm + */ + +#ifndef _TextFieldP_H +#define _TextFieldP_H + +#include + +#include "TextField.h" + +#define TEXTFIELD_ALLOC_SIZE 256 + +typedef struct { + int dummy; /* keep compiler happy with dummy field */ +} TextFieldClassPart; + +typedef struct _TextFieldClassRec { + CoreClassPart core_class; + TextFieldClassPart TextField_class; +} TextFieldClassRec; + +extern TextFieldClassRec textfieldClassRec; + +typedef struct { + /* Public stuff ... */ + long foreground_pixel; /* data storage for resources ... */ + long cursor_pixel; + XFontStruct *font; + Dimension Margin; + int TextMaxLen; + Boolean Echo; + Boolean Editable; + Boolean DisplayCursor; + Boolean AllowSelection; + Boolean PendingDelete; + char *DefaultString; + XtCallbackList ActivateCallback; + + /* Private stuff ... */ + GC drawGC; /* GC for drawing and copying */ + GC highlightGC; /* GC for highlighting text */ + GC cursorGC; /* GC for cursor (not clipped like drawGC) */ + GC dashGC; /* GC for cursor when we don't have focus */ + GC eraseGC; /* GC for erasing (not clipped) */ + + int CursorPos; /* text position of cursor */ + int OldCursorPos; /* previous position */ + int OldCursorX; /* previous pixel pos of cursor */ + int HighlightStart; /* text pos of leftmost highlight pos */ + int HighlightEnd; /* text pos of rightmost highlight pos */ + int HighlightPivotStart; /* left pivot pos for ExtendHighlight */ + int HighlightPivotEnd; /* right ... */ + int OldHighlightStart; /* save data */ + int OldHighlightEnd; + + char *Text; /* pointer to the text */ + int TextAlloc; /* number of bytes allocated for the text */ + int TextLen; /* current length of text */ + + char *SelectionText; /* pointer to text selection, when needed */ + int SelectionLen; /* length */ + + int FastInsertCursorStart; /* data storage for some text optimization */ + int FastInsertTextLen; + + Dimension ViewWidth; /* visible width of widget */ + int XOffset; /* offset from x=0 to start of text string */ + int OldXOffset; + int YOffset; /* y pixel offset to baseline of font */ + int TextWidth; /* char width of text */ + int OldTextWidth; + + XtIntervalId timer_id; /* timer for double click test */ + int timer_x; /* save event x pos */ + int highlight_time; /* time delay for scrolling */ + int multi_click_time; /* local storage for XtGetMultiClickTime */ +} TextFieldPart; + +typedef struct _TextFieldRec { + CorePart core; + TextFieldPart text; +} TextFieldRec; + + +#endif /* _TextFieldP_H */ diff --git a/plain/Imakefile b/plain/Imakefile new file mode 100644 index 0000000..86e3f7f --- /dev/null +++ b/plain/Imakefile @@ -0,0 +1,14 @@ +#include <../Common.tmpl> + +SRCS = plain.c + +OBJS = plain.o + +EXTRA_DEFINES = $(CHIMERA_DEFINES) +EXTRA_INCLUDES = -I../port -I../mxw -I../common -I../chimera + +NormalLibraryTarget(xplain, $(OBJS)) + +DependTarget() + +install.man:: diff --git a/plain/plain.c b/plain/plain.c new file mode 100644 index 0000000..6b2f4af --- /dev/null +++ b/plain/plain.c @@ -0,0 +1,428 @@ +/* + * plain.c + * + * libplain - a bad plain text renderer + * + * Copyright (c) 1995-1997, John Kilburg + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ + +/* + * This sucks. + */ + +#include "port_before.h" + +#include + +#ifdef HAVE_STDLIB_H +#include +#endif + +#include + +#include "port_after.h" + +#include "Chimera.h" +#include "ChimeraRender.h" + +#define PLAIN_MARGIN 20 + +typedef struct +{ + char *s; + size_t len; + int y; + unsigned int width; +} LineInfoP, *LineInfo; + +typedef struct +{ + MemPool mp; + GList lines; + size_t off; + size_t len; + byte *data; + + XFontStruct *font; + Window win; + Widget w; + Display *dpy; + GC gc; + Pixel fg, bg; + + ChimeraSink wp; + ChimeraResources cres; + ChimeraGUI wd; + + /* Dimensions stuff */ + unsigned int lineSpace; + unsigned int baseline; + unsigned int lineheight; + unsigned int width, height; + + LineInfo searchline; + char *searchchar; + LineInfo searchcur; + char *searchstr; + size_t searchlen; +} PlainInfoP, *PlainInfo; + +static LineInfo MakeLine _ArgProto((PlainInfo, char *, size_t)); +static void ParsePlainData _ArgProto((PlainInfo)); +static int PlainSearch _ArgProto((void *, char *, int)); + +static LineInfo +MakeLine(pi, s, len) +PlainInfo pi; +char *s; +size_t len; +{ + LineInfo li; + + li = (LineInfo)MPCGet(pi->mp, sizeof(LineInfoP)); + li->s = s; + li->len = len; + li->y = pi->height; + pi->height += pi->lineheight; + + li->width = XTextWidth(pi->font, s, len); + if (li->width > pi->width) pi->width = li->width + PLAIN_MARGIN * 2; + + GUISetDimensions(pi->wd, pi->width, pi->height); + + XSetFont(pi->dpy, pi->gc, pi->font->fid); + XSetForeground(pi->dpy, pi->gc, pi->fg); + + XDrawString(pi->dpy, pi->win, pi->gc, PLAIN_MARGIN, li->y + pi->baseline, + li->s, + (int)li->len); /* what to do about this ? */ + + GListAddTail(pi->lines, li); + + return(li); +} + +static void +ParsePlainData(pi) +PlainInfo pi; +{ + char *fcp, *cp, *lcp; + MIMEHeader mh; + + SinkGetData(pi->wp, &(pi->data), &(pi->len), &mh); + + fcp = (char *)(pi->data + pi->off); + lcp = (char *)(pi->data + pi->len); + for (cp = fcp; cp < lcp; cp++) + { + if (*cp == '\r') + { + MakeLine(pi, fcp, cp - fcp); + pi->off = (byte *)cp - pi->data + 1; + fcp = cp + 1; + if (fcp < lcp && *fcp == '\n') + { + fcp++; + pi->off++; + } + } + else if (*cp == '\n') + { + MakeLine(pi, fcp, cp - fcp); + pi->off = (byte *)cp - pi->data + 1; + fcp = cp + 1; + } + } + + return; +} + +static void +PlainAdd(closure) +void *closure; +{ + ParsePlainData((PlainInfo)closure); + return; +} + +static void +PlainEnd(closure) +void *closure; +{ + PlainInfo pi = (PlainInfo)closure; + + ParsePlainData(pi); + + if (pi->off < pi->len) + { + MakeLine(pi, (char *)(pi->data + pi->off), pi->len - pi->off); + } + + pi->height += pi->lineheight; + GUISetDimensions(pi->wd, pi->width, pi->height); + + return; +} + +static bool +PlainExpose(closure, x, y, width, height) +void *closure; +int x, y; +unsigned int width, height; +{ + LineInfo li; + PlainInfo pi = (PlainInfo)closure; + Dimension swidth; + + XSetFont(pi->dpy, pi->gc, pi->font->fid); + XSetForeground(pi->dpy, pi->gc, pi->fg); + + for (li = (LineInfo)GListGetHead(pi->lines); li != NULL; + li = (LineInfo)GListGetNext(pi->lines)) + { + if (li == pi->searchcur) + { + XDrawString(pi->dpy, pi->win, pi->gc, PLAIN_MARGIN, li->y + pi->baseline, + li->s, pi->searchstr - li->s); + swidth = XTextWidth(pi->font, li->s, pi->searchstr - li->s); + + XSetForeground(pi->dpy, pi->gc, pi->bg); + XSetBackground(pi->dpy, pi->gc, pi->fg); + + XDrawString(pi->dpy, pi->win, pi->gc, + PLAIN_MARGIN + swidth, li->y + pi->baseline, + pi->searchstr, pi->searchlen); + + XSetForeground(pi->dpy, pi->gc, pi->fg); + XSetBackground(pi->dpy, pi->gc, pi->bg); + + swidth = XTextWidth(pi->font, pi->searchstr, pi->searchlen); + + XDrawString(pi->dpy, pi->win, pi->gc, + PLAIN_MARGIN + swidth, li->y + pi->baseline, + pi->searchstr + pi->searchlen, + li->len - (pi->searchstr - li->s) - pi->searchlen); + + } + else + { + XDrawString(pi->dpy, pi->win, pi->gc, + PLAIN_MARGIN, li->y + pi->baseline, + li->s, (int)li->len ); /* what to do about this ? */ + } + } + + return(true); +} + +static bool +PlainMouse(closure, x, y, action) +void *closure; +int x, y; +char *action; +{ + return(true); +} + +static bool +PlainMotion(closure, x, y) +void *closure; +int x, y; +{ + return(true); +} + +/* + * PlainQuery + */ +static byte * +PlainQuery(closure, key) +void *closure; +char *key; +{ + return(NULL); +} + +/* + * PlainSearch + */ +static int +PlainSearch(closure, data, mode) +void *closure; +char *data; +int mode; +{ + LineInfo li; + PlainInfo pi = (PlainInfo)closure; + char *cp, *lcp, *xcp, *dcp, *ldcp; + size_t dlen; + bool found; + + dlen = strlen(data); + for (li = (LineInfo)GListGetHead(pi->lines); li != NULL; + li = (LineInfo)GListGetNext(pi->lines)) + { + lcp = li->s + li->len; + for (cp = li->s; cp < lcp; cp++) + { + if (*cp == data[0]) + { + dcp = data; + ldcp = data + dlen; + found = true; + for (xcp = cp; dcp < ldcp && xcp < lcp; dcp++, xcp++) + { + if (*xcp != *dcp) + { + found = false; + break; + } + } + if (found && dcp == ldcp) + { + GUISetScrollPosition(pi->wd, 0, -(li->y)); + pi->searchcur = li; + pi->searchstr = cp; + pi->searchlen = dlen; + pi->searchline = li; + pi->searchchar = xcp; + return(0); + } + } + } + } + + return(-1); +} + +/* + * PlainDestroy + */ +static void +PlainDestroy(closure) +void *closure; +{ + PlainInfo pi = (PlainInfo)closure; + + MPDestroy(pi->mp); + + return; +} + +/* + * PlainCancel + */ +static void +PlainCancel(closure) +void *closure; +{ + return; +} + +/* + * PlainInit + */ +static void * +PlainInit(wn, closure, state) +ChimeraRender wn; +void *closure; +void *state; +{ + PlainInfo pi; + MemPool mp; + char *name; + unsigned int width, height; + ChimeraSink wp; + ChimeraGUI wd; + + wd = RenderToGUI(wn); + wp = RenderToSink(wn); + + mp = MPCreate(); + pi = (PlainInfo)MPCGet(mp, sizeof(PlainInfoP)); + pi->mp = mp; + pi->wp = wp; + pi->lineheight = 40; + pi->lines = GListCreateX(mp); + pi->wd = wd; + pi->cres = RenderToResources(wn); + + pi->dpy = GUIToDisplay(wd); + pi->win = GUIToWindow(wd); + pi->gc = DefaultGC(pi->dpy, DefaultScreen(pi->dpy)); + + pi->lineSpace = 2; + + name = ResourceGetString(pi->cres, "plain.font"); + if (name == NULL) name = "fixed"; + if ((pi->font = XLoadQueryFont(pi->dpy, name)) == NULL) + { + fprintf (stderr, "Could not get default font.\n"); + fflush(stderr); + return(NULL); + } + + pi->baseline = pi->font->ascent + pi->lineSpace; + pi->lineheight = pi->font->ascent + pi->font->descent + pi->lineSpace; + + pi->height = PLAIN_MARGIN; + + GUIGetNamedColor(wd, "black", &(pi->fg)); + pi->bg = GUIBackgroundPixel(wd); + + XSetForeground(pi->dpy, pi->gc, pi->fg); + + GUISetScrollBar(wd, true); + if (GUIGetDimensions(wd, &width, &height) == -1) + { + GUISetDimensions(wd, width, height); + /* Now grab the internal dimensions again */ + if (GUIGetDimensions(wd, &width, &height) == -1) + { + return(NULL); + } + } + + return(pi); +} + +/* + * InitModule_Plain + */ +int +InitModule_Plain(cres) +ChimeraResources cres; +{ + ChimeraRenderHooks rh; + + memset(&rh, 0, sizeof(rh)); + rh.class_destroy = NULL; + rh.content = "text/plain"; + rh.init = PlainInit; + rh.add = PlainAdd; + rh.end = PlainEnd; + rh.destroy = PlainDestroy; + rh.search = PlainSearch; + rh.query = PlainQuery; + rh.cancel = PlainCancel; + rh.expose = PlainExpose; + rh.select = PlainMouse; + rh.motion = PlainMotion; + RenderAddHooks(cres, &rh); + + return(0); +} diff --git a/port/Imakefile b/port/Imakefile new file mode 100644 index 0000000..7a5fdad --- /dev/null +++ b/port/Imakefile @@ -0,0 +1,31 @@ +#include <../Common.tmpl> + +#ifdef NEED_MEMMOVE +SRC1 = memmove.c +OBJ1 = memmove.o +#endif + +#ifdef NEED_GETCWD +SRC2 = getcwd.c +OBJ2 = getcwd.o +#endif + +#ifdef NEED_STRSTUFF +SRC3 = strcasecmp.c strtol.c strstr.c strtoul.c +OBJ3 = strcasecmp.o strtol.o strstr.o strtoul.o +#endif + +#ifndef HAVE_SNPRINTF +SRC5 = snprintf.c +OBJ5 = snprintf.o +#endif + +SRCS = junk.c $(SRC1) $(SRC2) $(SRC3) $(SRC5) +OBJS = junk.o $(OBJ1) $(OBJ2) $(OBJ3) $(OBJ5) + +EXTRA_DEFINES = $(CHIMERA_DEFINES) +EXTRA_INCLUDES = -I./ + +NormalLibraryTarget(xport, $(OBJS)) + +DependTarget() diff --git a/port/getcwd.c b/port/getcwd.c new file mode 100644 index 0000000..99bc1b8 --- /dev/null +++ b/port/getcwd.c @@ -0,0 +1,65 @@ +/* + * getcwd.c -- + * + * This file provides an implementation of the getcwd procedure + * that uses getwd, for systems with getwd but without getcwd. + * + * Copyright (c) 1993 The Regents of the University of California. + * All rights reserved. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT + * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF + * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + */ + +#ifndef lint +static char rcsid[] = "$Header: /user6/ouster/tcl/compat/RCS/getcwd.c,v 1.2 93/07/12 14:00:59 ouster Exp $ SPRITE (Berkeley)"; +#endif /* not lint */ + +#include +#include +#include + +extern char *getwd(); +extern int errno; + +char * +getcwd(buf, size) + char *buf; /* Where to put path for current directory. */ + int size; /* Number of bytes at buf. */ +{ + char realBuffer[MAXFILENAMELEN+1]; + int length; + + if (getwd(realBuffer) == NULL) { + /* + * There's not much we can do besides guess at an errno to + * use for the result (the error message in realBuffer isn't + * much use...). + */ + + errno = EACCES; + return NULL; + } + length = strlen(realBuffer); + if (length >= size) { + errno = ERANGE; + return NULL; + } + strcpy(buf, realBuffer); + return buf; +} + diff --git a/port/junk.c b/port/junk.c new file mode 100644 index 0000000..440baec --- /dev/null +++ b/port/junk.c @@ -0,0 +1,9 @@ +/* + * junk.c + * + * placeholder + */ +void +chimera_placeholder() +{ +} diff --git a/port/memmove.c b/port/memmove.c new file mode 100644 index 0000000..af8fa51 --- /dev/null +++ b/port/memmove.c @@ -0,0 +1,53 @@ +/* + * memmove.c + * + * grabbed from X11R6 Xbsd/Berklib.c + */ + +/* + +Copyright (c) 1987 X Consortium + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE X CONSORTIUM BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +Except as contained in this notice, the name of the X Consortium shall +not be used in advertising or otherwise to promote the sale, use or +other dealings in this Software without prior written authorization +from the X Consortium. + +*/ + +char *memmove (b1, b2, length) + register char *b1, *b2; + register int length; +{ + char *sb1 = b1; + + if (b2 < b1) { + b1 += length; + b2 += length; + while (length--) + *--b1 = *--b2; + } else { + while (length--) + *b1++ = *b2++; + } + return sb1; +} diff --git a/port/port_after.h b/port/port_after.h new file mode 100644 index 0000000..6337c38 --- /dev/null +++ b/port/port_after.h @@ -0,0 +1,150 @@ +/* + * port_after.h + * + * Copyright (c) 1996-1997, John Kilburg + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + * + * Should be included after all of the "system" header files. + */ + +#ifndef __PORT_AFTER_H__ +#define __PORT_AFTER_H__ 1 + +#include + +#ifdef HAVE_STDDEF_H +#include +#endif + +#ifdef HAVE_LIMITS_H +#include +#endif + +#ifdef __QNX__ +#include +#include /* for snprintf(), vsnprintf() */ +#define strcasecmp(s1, s2) stricmp(s1, s2) +#define strncasecmp(s1, s2, n) strnicmp(s1, s2, n) +#endif + +#ifdef __EMX__ +#define strcasecmp(s1, s2) stricmp(s1, s2) +#define strncasecmp(s1, s2, n) strnicmp(s1, s2, n) +#endif + +#ifndef HAVE_SIZE_T +typedef unsigned long size_t; +#endif + +#ifndef HAVE_SSIZE_T +typedef long ssize_t; +#endif + +/* + * ANSI-C stuff. Snarfed from xloadimage. + */ + +#ifdef __STDC__ +#if !defined(_ArgProto) +#define _ArgProto(ARGS) ARGS +#endif +#else /* !__STDC__ */ +/* "const" is an ANSI thing */ +#if !defined(const) +#define const +#endif + +#if !defined(_ArgProto) +#define _ArgProto(ARGS) () +#endif + +#endif /* __STDC__ */ + +/* + * If you have an old version of C++ without bool, you'll have to add + * the ifdef here + */ +#ifndef __cplusplus + +#if !defined(bool_DEFINED) && !defined(true_DEFINED) && !defined(false_DEFINED) +#define bool_DEFINED 1 +#define true_DEFINED 1 +#define false_DEFINED 1 +typedef enum +{ + false = 0, + true = 1 +} bool; +#endif + +#ifndef bool_DEFINED +#define bool_DEFINED 1 +typedef int bool; +#endif + +#ifndef true_DEFINED +#define true_DEFINED 1 +#define true 1 +#endif + +#ifndef false_DEFINED +#define false_DEFINED 1 +#define false 0 +#endif + +#endif /* __cplusplus */ + +#ifndef MAX +#define MAX(a,b) ((a) < (b) ? (b) : (a)) +#endif + +#ifndef MIN +#define MIN(a,b) ((a) < (b) ? (a) : (b)) +#endif + +/* + * should be in limits.h + */ +#ifndef CHAR_BITS +#define CHAR_BITS 8 +#endif + +#ifndef byte_DEFINED +#define byte_DEFINED 1 +typedef unsigned char byte; +#endif + +#ifndef word_DEFINED +#define word_DEFINED 1 +typedef unsigned long word; +#endif + +/* + * FILENAME_MAX apparently doesn't cut it. I don't see a decent + * alternative. Invent one. If it clashes or isn't long enough then + * try again. Repeat as necessary. + */ +#define MAXFILENAMELEN 1024 + +#define isspace8(a) ((a) < 33 && (a) >= 0) + +#ifndef HAVE_SNPRINTF +#ifdef HAVE_STDARG_H +int snprintf _ArgProto((char *, size_t, const char *, ...)); +#endif +#endif + +#endif diff --git a/port/port_before.h b/port/port_before.h new file mode 100644 index 0000000..ea16c3e --- /dev/null +++ b/port/port_before.h @@ -0,0 +1,13 @@ +/* + * port_before.h + * + * Should be the first header file included. Could eventually contain + * the HAVE_* defines to clean up the command line if necessary (maybe + * useful to clean up the Common.tmpl.dist file). Maybe useful for + * other weird situations. Inspired by the bind distribution. + */ + +#ifdef __PORT_BEFORE_H__ +#define __PORT_BEFORE_H__ + +#endif diff --git a/port/snprintf.c b/port/snprintf.c new file mode 100644 index 0000000..7c838e7 --- /dev/null +++ b/port/snprintf.c @@ -0,0 +1,354 @@ +/*************************************************************************** + * LPRng - An Extended Print Spooler System + * + * Copyright 1988-1995 Patrick Powell, San Diego State University + * papowell@sdsu.edu + * See LICENSE for conditions of use. + * + *************************************************************************** + * MODULE: snprintf.c + * PURPOSE: LPRng version of printf - absolutely bombproof (hopefully!) + **************************************************************************/ + +/* + * + * + * Removed the 'plp' from function names and may have made other + * modifications so if it looks mangled its my fault. + * + * -john + * + * + */ +#include + +#ifdef HAVE_STDARG_H +#include +#define HAVE_STDARGS /* let's hope that works everywhere (mj) */ +#define VA_LOCAL_DECL va_list ap; +#define VA_START(f) va_start(ap, f) +#define VA_SHIFT(v,t) ; /* no-op for ANSI */ +#define VA_END va_end(ap) +#else +#ifdef HAVE_VARARGS_H +#include +#undef HAVE_STDARGS +#define VA_LOCAL_DECL va_list ap; +#define VA_START(f) va_start(ap) /* f is ignored! */ +#define VA_SHIFT(v,t) v = va_arg(ap,t) +#define VA_END va_end(ap) +#else +XX ** NO VARARGS ** XX +#endif +#endif + +#ifdef HAVE_STRING_H +#include +#endif + +#include "port_after.h" + +/* + * dopr(): poor man's version of doprintf + */ + +static void dopr( char *buffer, const char *format, va_list args ); +static void fmtstr( char *value, int ljust, int len, int zpad, int precision ); +static void fmtnum( long value, int base, int dosign, + int ljust, int len, int zpad, int precision ); +static void fmtdouble( int fmt, double value, + int ljust, int len, int zpad, int precision ); +static void dostr( char * ); +static char *output; +static void dopr_outch( int c ); +static char *end; + +/************************************************************** + * Original: + * Patrick Powell Tue Apr 11 09:48:21 PDT 1995 + * A bombproof version of doprnt (dopr) included. + * Sigh. This sort of thing is always nasty do deal with. Note that + * the version here does not include floating point... + * + * plp_snprintf() is used instead of sprintf() as it does limit checks + * for string length. This covers a nasty loophole. + * + * The other functions are there to prevent NULL pointers from + * causing nast effects. + **************************************************************/ + +int vsnprintf(char *str, size_t count, const char *fmt, va_list args) +{ + str[0] = 0; + end = str+count-1; + dopr( str, fmt, args ); + if( count>0 ){ + end[0] = 0; + } + return(strlen(str)); +} + +/* VARARGS3 */ +#ifdef HAVE_STDARGS +int snprintf (char *str,size_t count,const char *fmt,...) +#else +int snprintf (va_alist) va_dcl +#endif +{ +#ifndef HAVE_STDARGS + char *str; + size_t count; + char *fmt; +#endif + VA_LOCAL_DECL + + VA_START (fmt); + VA_SHIFT (str, char *); + VA_SHIFT (count, size_t ); + VA_SHIFT (fmt, char *); + (void) vsnprintf ( str, count, fmt, ap); + VA_END; + return( strlen( str ) ); +} + +static void dopr( char *buffer, const char *format, va_list args ) +{ + int ch; + long value; + int longflag = 0; + char *strvalue; + int ljust; + int len; + int zpad; + int precision; + int set_precision; + double dval; + + output = buffer; + while( (ch = *format++) ){ + switch( ch ){ + case '%': + ljust = len = zpad = 0; + precision = -1; set_precision = 0; + nextch: + ch = *format++; + switch( ch ){ + case 0: + dostr( "**end of format**" ); + return; + case '-': ljust = 1; goto nextch; + case '.': set_precision = 1; precision = 0; goto nextch; + case '*': len = va_arg( args, int ); goto nextch; + case '0': /* set zero padding if len not set */ + if(len==0 && set_precision == 0 ) zpad = '0'; + case '1': case '2': case '3': + case '4': case '5': case '6': + case '7': case '8': case '9': + if( set_precision ){ + precision = precision*10 + ch - '0'; + } else { + len = len*10 + ch - '0'; + } + goto nextch; + case 'l': longflag = 1; goto nextch; + case 'u': case 'U': + /*fmtnum(value,base,dosign,ljust,len, zpad, precision) */ + if( longflag ){ + value = va_arg( args, long ); + } else { + value = va_arg( args, int ); + } + fmtnum( value, 10,0, ljust, len, zpad, precision ); break; + case 'o': case 'O': + /*fmtnum(value,base,dosign,ljust,len, zpad, precision) */ + if( longflag ){ + value = va_arg( args, long ); + } else { + value = va_arg( args, int ); + } + fmtnum( value, 8,0, ljust, len, zpad, precision ); break; + case 'd': case 'D': + if( longflag ){ + value = va_arg( args, long ); + } else { + value = va_arg( args, int ); + } + fmtnum( value, 10,1, ljust, len, zpad, precision ); break; + case 'x': + if( longflag ){ + value = va_arg( args, long ); + } else { + value = va_arg( args, int ); + } + fmtnum( value, 16,0, ljust, len, zpad, precision ); break; + case 'X': + if( longflag ){ + value = va_arg( args, long ); + } else { + value = va_arg( args, int ); + } + fmtnum( value,-16,0, ljust, len, zpad, precision ); break; + case 's': + strvalue = va_arg( args, char *); + fmtstr( strvalue,ljust,len, zpad, precision ); break; + case 'c': + ch = va_arg( args, int ); + { char b[2]; + b[0] = ch; + b[1] = 0; + fmtstr( b,ljust,len, zpad, precision ); break; + } + break; + case 'f': case 'g': + dval = va_arg( args, double ); + fmtdouble( ch, dval,ljust,len, zpad, precision ); break; + case '%': dopr_outch( ch ); continue; + default: + dostr( "???????" ); + } + longflag = 0; + break; + default: + dopr_outch( ch ); + break; + } + } + *output = 0; +} + +/* + * Format '%[-]len[.precision]s' + * - = left justify (ljust) + * len = minimum length + * precision = numbers of chars in string to use + */ +static void +fmtstr( char *value, int ljust, int len, int zpad, int precision ) +{ + int padlen, strlen, i; /* amount to pad */ + + if( value == 0 ){ + value = ""; + } + if( precision > 0 ){ + strlen = precision; + } else { + for( strlen = 0; value[strlen]; ++ strlen ); /* strlen */ + } + padlen = len - strlen; + if( padlen < 0 ) padlen = 0; + if( ljust ) padlen = -padlen; + while( padlen > 0 ) { + dopr_outch( ' ' ); + --padlen; + } + /* output characters */ + for( i = 0; i < strlen; ++i ) dopr_outch(value[i]); + while( padlen < 0 ) { + dopr_outch( ' ' ); + ++padlen; + } +} + +static void +fmtnum( long value, int base, int dosign, int ljust, + int len, int zpad, int precision ) +{ + int signvalue = 0; + unsigned long uvalue; + char convert[20]; + int place = 0; + int padlen = 0; /* amount to pad */ + int caps = 0; + + /* DEBUGP(("value 0x%x, base %d, dosign %d, ljust %d, len %d, zpad %d\n", + value, base, dosign, ljust, len, zpad )); */ + uvalue = value; + if( dosign ){ + if( value < 0 ) { + signvalue = '-'; + uvalue = -value; + } + } + if( base < 0 ){ + caps = 1; + base = -base; + } + do{ + convert[place++] = + (caps? "0123456789ABCDEF":"0123456789abcdef") + [uvalue % (unsigned)base ]; + uvalue = (uvalue / (unsigned)base ); + }while(uvalue); + convert[place] = 0; + padlen = len - place; + if( padlen < 0 ) padlen = 0; + if( ljust ) padlen = -padlen; + /* DEBUGP(( "str '%s', place %d, sign %c, padlen %d\n", + convert,place,signvalue,padlen)); */ + if( zpad && padlen > 0 ){ + if( signvalue ){ + dopr_outch( signvalue ); + --padlen; + signvalue = 0; + } + while( padlen > 0 ){ + dopr_outch( zpad ); + --padlen; + } + } + while( padlen > 0 ) { + dopr_outch( ' ' ); + --padlen; + } + if( signvalue ) dopr_outch( signvalue ); + while( place > 0 ) dopr_outch( convert[--place] ); + while( padlen < 0 ){ + dopr_outch( ' ' ); + ++padlen; + } +} + +static void +fmtdouble( int fmt, double value, int ljust, int len, int zpad, int precision ) +{ + char convert[128]; + char fmtstr[128]; + int l; + + if( len == 0 ) len = 10; + if( len > sizeof(convert) - 10 ){ + len = sizeof(convert) - 10; + } + if( precision > sizeof(convert) - 10 ){ + precision = sizeof(convert) - 10; + } + if( precision > len ) precision = len; + strcpy( fmtstr, "%" ); + if( ljust ) strcat(fmtstr, "-" ); + if( len ){ + sprintf( fmtstr+strlen(fmtstr), "%d", len ); + } + if( precision > 0 ){ + sprintf( fmtstr+strlen(fmtstr), ".%d", precision ); + } + l = strlen( fmtstr ); + fmtstr[l] = fmt; + fmtstr[l+1] = 0; + sprintf( convert, fmtstr, value ); + dostr( convert ); +} + +static void dostr( char *str ) +{ + while(*str) dopr_outch(*str++); +} + +static void dopr_outch( int c ) +{ + if( end == 0 || output < end ){ + *output++ = c; + } +} + + diff --git a/port/strcasecmp.c b/port/strcasecmp.c new file mode 100644 index 0000000..030c6c1 --- /dev/null +++ b/port/strcasecmp.c @@ -0,0 +1,115 @@ +/* + * Copyright (c) 1987 Regents of the University of California. + * All rights reserved. + * + * Redistribution and use in source and binary forms, with or without + * modification, are permitted provided that the following conditions + * are met: + * 1. Redistributions of source code must retain the above copyright + * notice, this list of conditions and the following disclaimer. + * 2. Redistributions in binary form must reproduce the above copyright + * notice, this list of conditions and the following disclaimer in the + * documentation and/or other materials provided with the distribution. + * 3. All advertising materials mentioning features or use of this software + * must display the following acknowledgement: + * This product includes software developed by the University of + * California, Berkeley and its contributors. + * 4. Neither the name of the University nor the names of its contributors + * may be used to endorse or promote products derived from this software + * without specific prior written permission. + * + * THIS SOFTWARE IS PROVIDED BY THE REGENTS AND CONTRIBUTORS ``AS IS'' AND + * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE + * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE + * ARE DISCLAIMED. IN NO EVENT SHALL THE REGENTS OR CONTRIBUTORS BE LIABLE + * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL + * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS + * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) + * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY + * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF + * SUCH DAMAGE. + */ + +#include +#include + +#if defined(LIBC_SCCS) && !defined(lint) +static const char sccsid[] = "@(#)strcasecmp.c 5.10 (Berkeley) 1/26/91"; +#endif /* LIBC_SCCS and not lint */ + +typedef unsigned char u_char; + +/* + * This array is designed for mapping upper and lower case letter + * together for a case independent comparison. The mappings are + * based upon ascii character sequences. + */ +static const u_char charmap[] = { + '\000', '\001', '\002', '\003', '\004', '\005', '\006', '\007', + '\010', '\011', '\012', '\013', '\014', '\015', '\016', '\017', + '\020', '\021', '\022', '\023', '\024', '\025', '\026', '\027', + '\030', '\031', '\032', '\033', '\034', '\035', '\036', '\037', + '\040', '\041', '\042', '\043', '\044', '\045', '\046', '\047', + '\050', '\051', '\052', '\053', '\054', '\055', '\056', '\057', + '\060', '\061', '\062', '\063', '\064', '\065', '\066', '\067', + '\070', '\071', '\072', '\073', '\074', '\075', '\076', '\077', + '\100', '\141', '\142', '\143', '\144', '\145', '\146', '\147', + '\150', '\151', '\152', '\153', '\154', '\155', '\156', '\157', + '\160', '\161', '\162', '\163', '\164', '\165', '\166', '\167', + '\170', '\171', '\172', '\133', '\134', '\135', '\136', '\137', + '\140', '\141', '\142', '\143', '\144', '\145', '\146', '\147', + '\150', '\151', '\152', '\153', '\154', '\155', '\156', '\157', + '\160', '\161', '\162', '\163', '\164', '\165', '\166', '\167', + '\170', '\171', '\172', '\173', '\174', '\175', '\176', '\177', + '\200', '\201', '\202', '\203', '\204', '\205', '\206', '\207', + '\210', '\211', '\212', '\213', '\214', '\215', '\216', '\217', + '\220', '\221', '\222', '\223', '\224', '\225', '\226', '\227', + '\230', '\231', '\232', '\233', '\234', '\235', '\236', '\237', + '\240', '\241', '\242', '\243', '\244', '\245', '\246', '\247', + '\250', '\251', '\252', '\253', '\254', '\255', '\256', '\257', + '\260', '\261', '\262', '\263', '\264', '\265', '\266', '\267', + '\270', '\271', '\272', '\273', '\274', '\275', '\276', '\277', + '\300', '\301', '\302', '\303', '\304', '\305', '\306', '\307', + '\310', '\311', '\312', '\313', '\314', '\315', '\316', '\317', + '\320', '\321', '\322', '\323', '\324', '\325', '\326', '\327', + '\330', '\331', '\332', '\333', '\334', '\335', '\336', '\337', + '\340', '\341', '\342', '\343', '\344', '\345', '\346', '\347', + '\350', '\351', '\352', '\353', '\354', '\355', '\356', '\357', + '\360', '\361', '\362', '\363', '\364', '\365', '\366', '\367', + '\370', '\371', '\372', '\373', '\374', '\375', '\376', '\377', +}; + +int +strcasecmp(s1, s2) + const char *s1, *s2; +{ + register const u_char *cm = charmap, + *us1 = (const u_char *)s1, + *us2 = (const u_char *)s2; + + while (cm[*us1] == cm[*us2++]) + if (*us1++ == '\0') + return (0); + return (cm[*us1] - cm[*--us2]); +} + +int +strncasecmp(s1, s2, n) + const char *s1, *s2; + register size_t n; +{ + if (n != 0) { + register const u_char *cm = charmap, + *us1 = (const u_char *)s1, + *us2 = (const u_char *)s2; + + do { + if (cm[*us1] != cm[*us2++]) + return (cm[*us1] - cm[*--us2]); + if (*us1++ == '\0') + break; + } while (--n != 0); + } + return (0); +} diff --git a/port/strstr.c b/port/strstr.c new file mode 100644 index 0000000..4fd5e1b --- /dev/null +++ b/port/strstr.c @@ -0,0 +1,84 @@ +/* + * strstr.c -- + * + * Source code for the "strstr" library routine. + * + * Copyright (c) 1988-1993 The Regents of the University of California. + * All rights reserved. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT + * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF + * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + */ + +#ifndef lint +static char rcsid[] = "$Header: /user6/ouster/tcl/compat/RCS/strstr.c,v 1.2 93/03/19 15:25:40 ouster Exp $ SPRITE (Berkeley)"; +#endif /* not lint */ + +/* + *---------------------------------------------------------------------- + * + * strstr -- + * + * Locate the first instance of a substring in a string. + * + * Results: + * If string contains substring, the return value is the + * location of the first matching instance of substring + * in string. If string doesn't contain substring, the + * return value is 0. Matching is done on an exact + * character-for-character basis with no wildcards or special + * characters. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +char * +strstr(string, substring) + register char *string; /* String to search. */ + char *substring; /* Substring to try to find in string. */ +{ + register char *a, *b; + + /* First scan quickly through the two strings looking for a + * single-character match. When it's found, then compare the + * rest of the substring. + */ + + b = substring; + if (*b == 0) { + return string; + } + for ( ; *string != 0; string += 1) { + if (*string != *b) { + continue; + } + a = string; + while (1) { + if (*b == 0) { + return string; + } + if (*a++ != *b++) { + break; + } + } + b = substring; + } + return (char *) 0; +} diff --git a/port/strtol.c b/port/strtol.c new file mode 100644 index 0000000..622d48c --- /dev/null +++ b/port/strtol.c @@ -0,0 +1,99 @@ +/* + * strtol.c -- + * + * Source code for the "strtol" library procedure. + * + * Copyright (c) 1988 The Regents of the University of California. + * All rights reserved. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT + * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF + * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + */ + +#ifndef lint +static char rcsid[] = "$Header: /user6/ouster/tcl/compat/RCS/strtol.c,v 1.2 93/03/19 15:25:43 ouster Exp $ SPRITE (Berkeley)"; +#endif /* not lint */ + +#include + + +/* + *---------------------------------------------------------------------- + * + * strtol -- + * + * Convert an ASCII string into an integer. + * + * Results: + * The return value is the integer equivalent of string. If endPtr + * is non-NULL, then *endPtr is filled in with the character + * after the last one that was part of the integer. If string + * doesn't contain a valid integer value, then zero is returned + * and *endPtr is set to string. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +long int +strtol(string, endPtr, base) + char *string; /* String of ASCII digits, possibly + * preceded by white space. For bases + * greater than 10, either lower- or + * upper-case digits may be used. + */ + char **endPtr; /* Where to store address of terminating + * character, or NULL. */ + int base; /* Base for conversion. Must be less + * than 37. If 0, then the base is chosen + * from the leading characters of string: + * "0x" means hex, "0" means octal, anything + * else means decimal. + */ +{ + register char *p; + int result; + + /* + * Skip any leading blanks. + */ + + p = string; + while (isspace8(*p)) { + p += 1; + } + + /* + * Check for a sign. + */ + + if (*p == '-') { + p += 1; + result = -(strtoul(p, endPtr, base)); + } else { + if (*p == '+') { + p += 1; + } + result = strtoul(p, endPtr, base); + } + if ((result == 0) && (endPtr != 0) && (*endPtr == p)) { + *endPtr = string; + } + return result; +} diff --git a/port/strtoul.c b/port/strtoul.c new file mode 100644 index 0000000..68c7ea2 --- /dev/null +++ b/port/strtoul.c @@ -0,0 +1,199 @@ +/* + * strtoul.c -- + * + * Source code for the "strtoul" library procedure. + * + * Copyright (c) 1988 The Regents of the University of California. + * All rights reserved. + * + * Permission is hereby granted, without written agreement and without + * license or royalty fees, to use, copy, modify, and distribute this + * software and its documentation for any purpose, provided that the + * above copyright notice and the following two paragraphs appear in + * all copies of this software. + * + * IN NO EVENT SHALL THE UNIVERSITY OF CALIFORNIA BE LIABLE TO ANY PARTY FOR + * DIRECT, INDIRECT, SPECIAL, INCIDENTAL, OR CONSEQUENTIAL DAMAGES ARISING OUT + * OF THE USE OF THIS SOFTWARE AND ITS DOCUMENTATION, EVEN IF THE UNIVERSITY OF + * CALIFORNIA HAS BEEN ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + * + * THE UNIVERSITY OF CALIFORNIA SPECIFICALLY DISCLAIMS ANY WARRANTIES, + * INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY + * AND FITNESS FOR A PARTICULAR PURPOSE. THE SOFTWARE PROVIDED HEREUNDER IS + * ON AN "AS IS" BASIS, AND THE UNIVERSITY OF CALIFORNIA HAS NO OBLIGATION TO + * PROVIDE MAINTENANCE, SUPPORT, UPDATES, ENHANCEMENTS, OR MODIFICATIONS. + */ + +#ifndef lint +static char rcsid[] = "$Header: /user6/ouster/tcl/compat/RCS/strtoul.c,v 1.3 93/03/19 15:25:41 ouster Exp $ SPRITE (Berkeley)"; +#endif /* not lint */ + +#include + +/* + * The table below is used to convert from ASCII digits to a + * numerical equivalent. It maps from '0' through 'z' to integers + * (100 for non-digit characters). + */ + +static char cvtIn[] = { + 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, /* '0' - '9' */ + 100, 100, 100, 100, 100, 100, 100, /* punctuation */ + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, /* 'A' - 'Z' */ + 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, + 30, 31, 32, 33, 34, 35, + 100, 100, 100, 100, 100, 100, /* punctuation */ + 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, /* 'a' - 'z' */ + 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, + 30, 31, 32, 33, 34, 35}; + +/* + *---------------------------------------------------------------------- + * + * strtoul -- + * + * Convert an ASCII string into an integer. + * + * Results: + * The return value is the integer equivalent of string. If endPtr + * is non-NULL, then *endPtr is filled in with the character + * after the last one that was part of the integer. If string + * doesn't contain a valid integer value, then zero is returned + * and *endPtr is set to string. + * + * Side effects: + * None. + * + *---------------------------------------------------------------------- + */ + +unsigned long int +strtoul(string, endPtr, base) + char *string; /* String of ASCII digits, possibly + * preceded by white space. For bases + * greater than 10, either lower- or + * upper-case digits may be used. + */ + char **endPtr; /* Where to store address of terminating + * character, or NULL. */ + int base; /* Base for conversion. Must be less + * than 37. If 0, then the base is chosen + * from the leading characters of string: + * "0x" means hex, "0" means octal, anything + * else means decimal. + */ +{ + register char *p; + register unsigned long int result = 0; + register unsigned digit; + int anyDigits = 0; + + /* + * Skip any leading blanks. + */ + + p = string; + while (isspace8(*p)) { + p += 1; + } + + /* + * If no base was provided, pick one from the leading characters + * of the string. + */ + + if (base == 0) + { + if (*p == '0') { + p += 1; + if (*p == 'x') { + p += 1; + base = 16; + } else { + + /* + * Must set anyDigits here, otherwise "0" produces a + * "no digits" error. + */ + + anyDigits = 1; + base = 8; + } + } + else base = 10; + } else if (base == 16) { + + /* + * Skip a leading "0x" from hex numbers. + */ + + if ((p[0] == '0') && (p[1] == 'x')) { + p += 2; + } + } + + /* + * Sorry this code is so messy, but speed seems important. Do + * different things for base 8, 10, 16, and other. + */ + + if (base == 8) { + for ( ; ; p += 1) { + digit = *p - '0'; + if (digit > 7) { + break; + } + result = (result << 3) + digit; + anyDigits = 1; + } + } else if (base == 10) { + for ( ; ; p += 1) { + digit = *p - '0'; + if (digit > 9) { + break; + } + result = (10*result) + digit; + anyDigits = 1; + } + } else if (base == 16) { + for ( ; ; p += 1) { + digit = *p - '0'; + if (digit > ('z' - '0')) { + break; + } + digit = cvtIn[digit]; + if (digit > 15) { + break; + } + result = (result << 4) + digit; + anyDigits = 1; + } + } else { + for ( ; ; p += 1) { + digit = *p - '0'; + if (digit > ('z' - '0')) { + break; + } + digit = cvtIn[digit]; + if (digit >= base) { + break; + } + result = result*base + digit; + anyDigits = 1; + } + } + + /* + * See if there were any digits at all. + */ + + if (!anyDigits) { + p = string; + } + + if (endPtr != 0) { + *endPtr = p; + } + + return result; +} diff --git a/proto/Imakefile b/proto/Imakefile new file mode 100644 index 0000000..ff46db8 --- /dev/null +++ b/proto/Imakefile @@ -0,0 +1,14 @@ +#include <../Common.tmpl> + +SRCS = file.c http.c ftp.c mailto.c + +OBJS = file.o http.o ftp.o mailto.o + +INCLUDES = -I. -I../port -I../common -I../chimera + +EXTRA_DEFINES = $(CHIMERA_DEFINES) + +NormalLibraryTarget(xproto, $(OBJS)) +DependTarget() + +install.man:: diff --git a/proto/file.c b/proto/file.c new file mode 100644 index 0000000..28c5a1e --- /dev/null +++ b/proto/file.c @@ -0,0 +1,496 @@ +/* + * file.c + * + * Copyright (c) 1995-1997, John Kilburg + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +#include "port_before.h" + +#include +#include + +#ifdef HAVE_STDLIB_H +#include +#endif + +#ifdef HAVE_STRING_H +#include +#endif + +#ifdef HAVE_UNISTD_H +#include +#endif + +#include +#include + +#if defined(SYSV) || defined(SVR4) || defined(__arm) || defined(__QNX__) +#include +#define DIRSTUFF struct dirent +#else +#include +#define DIRSTUFF struct direct +#endif + +#include "port_after.h" + +#include "Chimera.h" +#include "ChimeraSource.h" +#include "ChimeraAuth.h" +#include "mime.h" + +#define PRINT_RATE 10 +#define MSGLEN 4096 /* this needs to be big */ + +/* Jim Rees fix */ +#ifndef S_ISDIR +#define S_ISDIR(mode) (((mode) & S_IFMT) == S_IFDIR) +#endif + +#ifndef S_ISBLK +#define S_ISBLK(mode) (((mode) & S_IFMT) == S_IFBLK) +#endif + +#ifndef S_ISREG +#define S_ISREG(mode) (((mode) & S_IFMT) == S_IFREG) +#endif + +#ifndef S_ISFIFO +#define S_ISFIFO(mode) (((mode) & S_IFMT) == S_IFIFO) +#endif + +#ifndef S_ISCHR +#define S_ISCHR(mode) (((mode) & S_IFMT) == S_IFCHR) +#endif + +/* not POSIX but what the heck */ +#ifndef S_ISLNK +#define S_ISLNK(mode) (((mode) & S_IFMT) == S_IFLNK) +#endif + +typedef struct +{ + FILE *fp; + off_t size; +} RegInfo; + +typedef struct +{ + DIR *dp; + int size; + int used; + char *dirname; + char **sa; + char *direntry; +} DirInfo; + +typedef struct +{ + MemPool mp; + bool directory; + bool stopped; + ChimeraSource ws; + ChimeraResources cres; + ChimeraTask wt; + ChimeraRequest *wr; + + char *filename; + + DirInfo di; + RegInfo ri; + bool init_done; + char mbuf[MSGLEN]; + char *rstr; + int pcount; + + byte *data; + size_t len; + MIMEHeader mh; +} FileInfo; + +static void DirRead _ArgProto((void *)); +static void FileRead _ArgProto((void *)); +static void FileGetData _ArgProto((void *, byte **, size_t *, MIMEHeader *)); +static int local_strcmp _ArgProto((const void *, const void *)); +static void DirEOF _ArgProto((FileInfo *)); +static void FileCancel _ArgProto((void *)); +static void FileDestroy _ArgProto((void *)); +static void *FileInit _ArgProto((ChimeraSource, ChimeraRequest *, void *)); +void InitModule_File _ArgProto((ChimeraResources)); + +static int +local_strcmp(a, b) +const void *a, *b; +{ + return(strcmp(*((char **)a), *((char **)b))); +} + +/* + * DirEOF + */ +static void +DirEOF(fi) +FileInfo *fi; +{ + int i; + char *f; + char **sa = fi->di.sa; + char *cp; + int flen; + static char *header1 = ""; + static char *header2 = "
      \n"; + static char *trailer1 = "\n
    "; + static char *format = "
  • %s%s\n"; + + if (fi->di.used > 0) qsort(sa, fi->di.used, sizeof(char *), local_strcmp); + + for (i = 0, flen = 0; i < fi->di.used; i++) + { + flen += 2 * strlen(sa[i]) + strlen(format) + strlen(fi->di.dirname); + } + flen += strlen(header1) + strlen(header2) + strlen(trailer1) + sizeof(char); + + f = (char *)alloc_mem(flen); + strcpy(f, header1); + strcat(f, header2); + for (i = 0; i < fi->di.used; i++) + { + for (cp = sa[i]; *cp != '\0'; cp++) + { + if (isspace8(*cp)) break; + } + *cp++ = '\0'; + snprintf(f + strlen(f), flen - strlen(f), + format, fi->di.dirname, sa[i], sa[i], cp); + } + strcat(f, trailer1); + + fi->data = (byte *)f; + fi->len = strlen(f); + fi->init_done = true; + SourceInit(fi->ws, false); + if (!fi->stopped) SourceEnd(fi->ws); + + return; +} + +/* + * DirRead + */ +static void +DirRead(closure) +void *closure; +{ + FileInfo *fi = (FileInfo *)closure; + DIRSTUFF *de; + int salen; + struct stat fs; + + if ((de = readdir(fi->di.dp)) == NULL) + { + closedir(fi->di.dp); + fi->di.dp = NULL; + DirEOF(fi); + fi->wt = NULL; + return; + } + + strcpy(fi->mbuf, fi->di.dirname); + strcat(fi->mbuf, de->d_name); + if (stat(fi->mbuf, &fs) != -1) + { + if (S_ISREG(fs.st_mode)) + { + snprintf(fi->mbuf, sizeof(fi->mbuf), " (%ld bytes)", (long)fs.st_size); + } + else if (S_ISDIR(fs.st_mode)) strcpy(fi->mbuf, "/"); +#ifndef __EMX__ + else if (S_ISLNK(fs.st_mode)) strcpy(fi->mbuf, " <link>"); + else if (S_ISFIFO(fs.st_mode)) strcpy(fi->mbuf, " <pipe>"); + else if (S_ISBLK(fs.st_mode)) strcpy(fi->mbuf, " <block device>"); +#endif + else if (S_ISCHR(fs.st_mode)) strcpy(fi->mbuf, " <char device>"); + else strcpy(fi->mbuf, " <unknown>"); + } + else strcpy(fi->mbuf, " "); + + /* + * Resize the file entry table if needed. + */ + if (fi->di.used >= fi->di.size) + { + char **nsa; + nsa = (char **)MPGet(fi->mp, fi->di.size * 2 * sizeof(char *)); + memcpy(nsa, fi->di.sa, fi->di.size * sizeof(char *)); + fi->di.size *= 2; + fi->di.sa = nsa; + } + + salen = strlen(de->d_name) + strlen(fi->mbuf) + 2 * sizeof(char); + fi->di.sa[fi->di.used] = (char *)MPGet(fi->mp, salen); + strcpy(fi->di.sa[fi->di.used], de->d_name); + strcat(fi->di.sa[fi->di.used], " "); + strcat(fi->di.sa[fi->di.used], fi->mbuf); + fi->di.used++; + + if (fi->pcount++ % PRINT_RATE == 0) + { + snprintf (fi->mbuf, sizeof(fi->mbuf), + fi->rstr, fi->di.used, fi->filename); + SourceSendMessage(fi->ws, fi->mbuf); + } + + fi->wt = TaskSchedule(fi->cres, DirRead, fi); + + return; +} + +/* + * FileRead + */ +static void +FileRead(closure) +void *closure; +{ + int readlen; + FileInfo *fi = (FileInfo *)closure; + + readlen = fi->ri.size - fi->len; + if (readlen > BUFSIZ) readlen = BUFSIZ; + + if ((readlen = fread(fi->data + fi->len, 1, readlen, fi->ri.fp)) == 0) + { + fclose(fi->ri.fp); + fi->ri.fp = NULL; + if (fi->init_done) SourceEnd(fi->ws); + + fi->wt = NULL; + } + else + { + if (fi->len == 0) + { + fi->init_done = true; + SourceInit(fi->ws, false); + if (fi->stopped) return; + } + fi->len += readlen; + SourceAdd(fi->ws); + + if (fi->pcount++ % PRINT_RATE == 0) + { + snprintf (fi->mbuf, sizeof(fi->mbuf), + fi->rstr, fi->len, fi->filename); + SourceSendMessage(fi->ws, fi->mbuf); + } + + fi->wt = TaskSchedule(fi->cres, FileRead, fi); + } + + return; +} + +/* + * FileCancel + */ +static void +FileCancel(closure) +void *closure; +{ + FileInfo *fi = (FileInfo *)closure; + + fi->stopped = true; + if (fi->wt != NULL) + { + TaskRemove(fi->cres, fi->wt); + fi->wt = NULL; + } + if (fi->di.dp != NULL) + { + closedir(fi->di.dp); + fi->di.dp = NULL; + } + if (fi->ri.fp != NULL) + { + fclose(fi->ri.fp); + fi->ri.fp = NULL; + } + + return; +} + +/* + * FileDestroy + */ +static void +FileDestroy(closure) +void *closure; +{ + FileInfo *fi = (FileInfo *)closure; + + FileCancel(fi); + if (fi->data != NULL) free_mem(fi->data); + if (fi->mh != NULL) MIMEDestroyHeader(fi->mh); + MPDestroy(fi->mp); + + return; +} + +/* + * FileInit + */ +static void * +FileInit(ws, wr, class_closure) +ChimeraSource ws; +ChimeraRequest *wr; +void *class_closure; +{ + FileInfo *fi; + struct stat s, as, *rs; + ChimeraTaskProc func; + char *rname, *drname; + char *content; + MemPool mp; + char *autofile, *autopath, *tpath; + size_t len; + char *filename; + + if (wr->up->filename == NULL) filename = "/"; + else filename = wr->up->filename; + + if (stat(filename, &s) == -1) return(NULL); + rs = &s; + tpath = filename; + + mp = MPCreate(); + fi = (FileInfo *)MPCGet(mp, sizeof(FileInfo)); + fi->mp = mp; + fi->ws = ws; + fi->cres = SourceToResources(ws); + fi->wr = wr; + fi->mh = MIMECreateHeader(); + fi->filename = MPStrDup(mp, filename); + + if (S_ISDIR(s.st_mode) && + (autofile = ResourceGetString(fi->cres, "file.autoLoad")) != NULL) + { + len = strlen(filename); + autopath = (char *)MPGet(mp, len + strlen(autofile) + strlen("/") + 1); + strcpy(autopath, fi->filename); + if (fi->filename[len - 1] != '/') strcat(autopath, "/"); + strcat(autopath, autofile); + + if (stat(autopath, &as) == 0) + { + tpath = autopath; + rs = &as; + } + } + + if (S_ISDIR(rs->st_mode)) + { + fi->directory = true; + if ((fi->di.dp = opendir(fi->filename)) == NULL) + { + FileCancel(fi); + return(NULL); + } + + fi->di.dirname = (char *)MPGet(fi->mp, strlen(fi->filename) + + strlen("/") + sizeof(char)); + strcpy(fi->di.dirname, fi->filename); + if (fi->filename[strlen(fi->filename) - 1] != '/') + { + strcat(fi->di.dirname, "/"); + } + + fi->di.size = 512; + fi->di.sa = (char **)MPGet(fi->mp, sizeof(char **) * fi->di.size); + + rname = "file.readdir"; + drname = "Read %d entries from %s"; + func = DirRead; + content = "text/html"; + } + else + { + fi->directory = false; + if ((fi->ri.fp = fopen(tpath, "r")) == NULL) + { + FileCancel(fi); + return(NULL); + } + + fi->data = (byte *)alloc_mem((size_t)rs->st_size); + fi->ri.size = rs->st_size; + + rname = "file.readfile"; + drname = "Read %d bytes from %s"; + func = FileRead; + if ((content = ChimeraExt2Content(fi->cres, tpath)) == NULL) + { + content = "text/plain"; + } + } + + MIMEAddField(fi->mh, "content-type", content); + MIMEAddField(fi->mh, "x-url", fi->wr->url); + + if ((fi->rstr = ResourceGetString(fi->cres, rname)) == NULL) + { + fi->rstr = drname; + } + + fi->wt = TaskSchedule(fi->cres, func, fi); + + return(fi); +} + +static void +FileGetData(closure, data, len, mh) +void *closure; +byte **data; +size_t *len; +MIMEHeader *mh; +{ + FileInfo *fi = (FileInfo *)closure; + + *data = fi->data; + *len = fi->len; + *mh = fi->mh; + + return; +} + +/* + * InitModule_File + */ +void +InitModule_File(cres) +ChimeraResources cres; +{ + ChimeraSourceHooks ph; + + memset(&ph, 0, sizeof(ph)); + ph.name = "file"; + ph.init = FileInit; + ph.destroy = FileDestroy; + ph.stop = FileCancel; + ph.getdata = FileGetData; + SourceAddHooks(cres, &ph); + + return; +} diff --git a/proto/ftp.c b/proto/ftp.c new file mode 100644 index 0000000..b6e70dd --- /dev/null +++ b/proto/ftp.c @@ -0,0 +1,970 @@ +/* + * ftp.c + * + * Copyright 1993-1997, John Kilburg (john@cs.unlv.edu) + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +#include "port_before.h" + +#include +#include + +#ifdef HAVE_STRING_H +#include +#endif + +#ifdef HAVE_STDLIB_H +#include +#endif + +#include "port_after.h" + +#include "Chimera.h" +#include "ChimeraStream.h" +#include "ChimeraSource.h" +#include "ChimeraAuth.h" +#include "mime.h" + +#define PRINT_RATE 10 + +#define FM_OPEN 0 +#define FM_RU 1 +#define FM_RK 2 +#define FM_DONE 3 +#define FM_SEND 4 +#define FM_WAITING 5 + +#define MSGLEN 1024 +#define REQLEN 1024 + +static struct ftp_message +{ + char *name; + char *def; +} ftp_messages[] = +{ + { "ftp.open", "Connecting to " }, + { "ftp.read_unknown", " bytes read so far." }, + { "ftp.read_known", " bytes remaining." }, + { "ftp.done", "FTP read finished." }, + { "ftp.send", "Sending request..." }, + { "ftp.waiting", "Waiting for a response from " }, + { NULL, NULL }, +}; + +typedef struct +{ + char *username; + char *password; + char *hostname; + int port; + bool good; +} FTPPassword; + +typedef struct +{ + MemPool mp; + char *header; + char *trailer; + GList passwords; +} FTPClass; + +typedef struct FTPInfoP FTPInfo; +typedef void (*FTPProc) _ArgProto((FTPInfo *)); +static void FTPDestroy _ArgProto((void *)); +static void FTPData _ArgProto((ChimeraStream, ssize_t, void *)); +static void FTPDirData _ArgProto((ChimeraStream, ssize_t, void *)); +static void FTPSimpleRead _ArgProto((ChimeraStream, ssize_t, void *)); +static void FTPDummy _ArgProto((ChimeraStream, ssize_t, void *)); +static void FTPDirRead _ArgProto((FTPInfo *)); +static void FTPNlst _ArgProto((FTPInfo *)); +static void FTPCwd _ArgProto((FTPInfo *)); +static void FTPRetrieve _ArgProto((FTPInfo *)); +static void FTPPassive _ArgProto((FTPInfo *)); +static void FTPSize _ArgProto((FTPInfo *)); +static void FTPType _ArgProto((FTPInfo *)); +static void FTPUser _ArgProto((FTPInfo *)); +static void FTPPass _ArgProto((FTPInfo *)); +static void *FTPInit _ArgProto((ChimeraSource, ChimeraRequest *, void *)); +static void FTPClassDestroy _ArgProto((void *)); +static void FTPCancel _ArgProto((void *)); +static void FTPGetData _ArgProto((void *, byte **, size_t *, MIMEHeader *)); +static int ftp_strcmp _ArgProto((const void *, const void *)); +static void FTPAuthCallback _ArgProto((void *, char *, char *)); +void InitModule_FTP _ArgProto((ChimeraResources)); + +struct FTPInfoP +{ + MemPool mp; + ChimeraSource ws; + ChimeraResources cres; + ChimeraRequest *wr; + FTPClass *fc; + FTPProc rfunc; + char msgbuf[MSGLEN]; + char request[REQLEN]; + char *msg[sizeof(ftp_messages) / sizeof(ftp_messages[0])]; + int rcount; + FTPPassword *fp; + ChimeraAuth wa; + + /* control stream */ + ChimeraStream cs; + byte *cb; /* control buffer */ + size_t cblen; /* control buffer content length */ + size_t cbsize; /* control buffer size */ + bool ignore_err; /* recognition of control errors */ + + /* data stream */ + ChimeraStream ds; + byte *db; /* data buffer */ + size_t dblen; /* data buffer content length */ + size_t dbsize; /* data buffer size */ + size_t dbmax; /* data buffer maximum */ + MIMEHeader mh; +}; + +static char *FTPDirToHTML _ArgProto((FTPInfo *)); +static void FTPWrite _ArgProto((ChimeraStream, ssize_t, void *)); +static void FTPDestroyStream _ArgProto((FTPInfo *)); +static void FTPReadData _ArgProto((FTPInfo *, ChimeraStreamCallback)); +static void FTPReadControl _ArgProto((FTPInfo *, ChimeraStreamCallback)); +static int FTPParseResponse _ArgProto((FTPInfo *)); +static void FTPDummy _ArgProto((ChimeraStream, ssize_t, void *)); +static void FTPFailure _ArgProto((FTPInfo *)); +static void FTPSimple _ArgProto((FTPInfo *, FTPProc, bool)); +static void FTPAddPassword _ArgProto((FTPInfo *, char *, char *)); +static FTPPassword *FTPFindPassword _ArgProto((FTPInfo *, char *)); + +/* + * FTPFindPassword + */ +static FTPPassword * +FTPFindPassword(fi, username) +FTPInfo *fi; +char *username; +{ + FTPPassword *fp; + char *hostname; + int port; + + hostname = fi->wr->up->hostname; + port = fi->wr->up->port == 0 ? 21:fi->wr->up->port; + + for (fp = (FTPPassword *)GListGetHead(fi->fc->passwords); fp != NULL; + fp = (FTPPassword *)GListGetNext(fi->fc->passwords)) + { + if (strcmp(username, fp->username) == 0 && + strcasecmp(hostname, fp->hostname) == 0 && + port == fp->port && + fp->good) + { + return(fp); + } + } + + return(NULL); +} + +/* + * FTPAddPassword + */ +static void +FTPAddPassword(fi, username, password) +FTPInfo *fi; +char *username; +char *password; +{ + FTPPassword *fp; + MemPool mp; + + if (username == NULL) return; + if (fi->wr->up->hostname == NULL) return; + + mp = fi->fc->mp; + + fp = (FTPPassword *)MPCGet(mp, sizeof(FTPPassword)); + fp->username = MPStrDup(mp, username); + if (password != NULL) fp->password = MPStrDup(mp, password); + else fp->password = MPStrDup(mp, ""); + fp->hostname = MPStrDup(mp, fi->wr->up->hostname); + fp->port = fi->wr->up->port == 0 ? 21:fi->wr->up->port; + GListAddHead(fi->fc->passwords, fp); + + fi->fp = fp; + + return; +} + +/* + * FTPFailure + */ +static void +FTPFailure(fi) +FTPInfo *fi; +{ + SourceStop(fi->ws, "ftp failure"); + FTPDestroyStream(fi); + return; +} + +/* + * FTPDestroyStream + */ +static void +FTPDestroyStream(fi) +FTPInfo *fi; +{ + if (fi->ds != NULL) + { + StreamDestroy(fi->ds); + fi->ds = NULL; + } + if (fi->cs != NULL) + { + StreamDestroy(fi->cs); + fi->cs = NULL; + } + return; +} + +/* + * FTPDestroy + */ +static void +FTPDestroy(closure) +void *closure; +{ + FTPInfo *fi = (FTPInfo *)closure; + FTPDestroyStream(fi); + if (fi->db != NULL) free_mem(fi->db); + if (fi->cb != NULL) free_mem(fi->cb); + if (fi->mh != NULL) MIMEDestroyHeader(fi->mh); + MPDestroy(fi->mp); + return; +} + +/* + * FTPReadData + */ +static void +FTPReadData(fi, func) +FTPInfo *fi; +ChimeraStreamCallback func; +{ + size_t len; + + if (fi->dbmax > 0) + { + len = fi->dbmax - fi->dblen; + if (len > BUFSIZ) len = BUFSIZ; + } + else len = BUFSIZ; + + if (len > 0) + { + if (fi->db == NULL) fi->db = (byte *)alloc_mem(len); + else fi->db = (byte *)realloc_mem(fi->db, fi->dbsize + len); + fi->dbsize += len; + } + + StreamRead(fi->ds, fi->db + fi->dblen, len, func, fi); + + return; +} + +/* + * FTPReadControl + */ +static void +FTPReadControl(fi, func) +FTPInfo *fi; +ChimeraStreamCallback func; +{ + if (fi->cblen + BUFSIZ > fi->cbsize) + { + if (fi->cb == NULL) fi->cb = (byte *)alloc_mem(BUFSIZ); + else fi->cb = (byte *)realloc_mem(fi->cb, fi->cbsize + BUFSIZ); + fi->cbsize += BUFSIZ; + } + StreamRead(fi->cs, fi->cb + fi->cblen, BUFSIZ, func, fi); + + return; +} + +/* + * FTPData + */ +static void +FTPData(ios, len, closure) +ChimeraStream ios; +ssize_t len; +void *closure; +{ + FTPInfo *fi = (FTPInfo *)closure; + + if (len < 0) + { + FTPFailure(fi); + return; + } + + if (len == 0) + { + SourceSendMessage(fi->ws, fi->msg[FM_DONE]); + FTPCancel(fi); + SourceEnd(fi->ws); + } + else + { + fi->dblen += len; + if (fi->dbmax > 0) + { + if (fi->rcount++ % PRINT_RATE == 0) + { + snprintf (fi->msgbuf, sizeof(fi->msgbuf), + "%ld %s", (long)(fi->dbmax - fi->dblen), fi->msg[FM_RK]); + SourceSendMessage(fi->ws, fi->msgbuf); + } + SourceAdd(fi->ws); + } + else + { + if (fi->rcount++ % PRINT_RATE == 0) + { + snprintf (fi->msgbuf, sizeof(fi->msgbuf), + "%ld %s", (long)fi->dblen, fi->msg[FM_RU]); + SourceSendMessage(fi->ws, fi->msgbuf); + } + } + FTPReadData(fi, FTPData); + } + + return; +} + +/* + * ftp_dirdata + */ +static void +FTPDirData(ios, len, closure) +ChimeraStream ios; +ssize_t len; +void *closure; +{ + FTPInfo *fi = (FTPInfo *)closure; + + if (len > 0) + { + fi->dblen += len; + FTPReadData(fi, FTPDirData); + return; + } + else if (len < 0) + { + FTPFailure(fi); + return; + } + + MIMEAddField(fi->mh, "content-type", "text/html"); + MIMEAddField(fi->mh, "x-url", fi->wr->url); + fi->db = FTPDirToHTML(fi); + fi->dblen = strlen(fi->db); + + FTPDestroyStream(fi); + + SourceInit(fi->ws, fi->fp == NULL); + SourceEnd(fi->ws); + + return; +} + +/* + * FTPParseResponse + */ +static int +FTPParseResponse(fi) +FTPInfo *fi; +{ + char *cp; + char *b = (char *)fi->cb; + size_t len = fi->cblen; + + if (b[len - 1] == '\n') + { + if (*(b + 3) == ' ') return(atoi(b)); + for (cp = b + len - 2; cp >= b; cp--) + { + if (*cp == '\n' && *(cp + 4) == ' ') return(atoi(cp + 1)); + } + } + + return(-1); +} + +/* + * FTPSimpleRead + */ +static void +FTPSimpleRead(ios, len, closure) +ChimeraStream ios; +ssize_t len; +void *closure; +{ + int ecode; + FTPInfo *fi = (FTPInfo *)closure; + + if (len < 0) + { + FTPFailure(fi); + return; + } + + fi->cblen += len; + + if ((ecode = FTPParseResponse(fi)) == -1) + { + FTPReadControl(fi, FTPSimpleRead); + return; + } + if (ecode < 400 || fi->ignore_err) + { + (fi->rfunc)(fi); + fi->cblen = 0; + } + else FTPFailure(fi); + + return; +} + +/* + * FTPWrite + */ +static void +FTPWrite(ios, len, closure) +ChimeraStream ios; +ssize_t len; +void *closure; +{ + FTPReadControl((FTPInfo *)closure, FTPSimpleRead); + return; +} + +/* + * FTPSimple + */ +static void +FTPSimple(fi, rfunc, ignore_err) +FTPInfo *fi; +FTPProc rfunc; +bool ignore_err; +{ + fi->rfunc = rfunc; + fi->ignore_err = ignore_err; + SourceSendMessage(fi->ws, fi->msg[FM_SEND]); + StreamWrite(fi->cs, (byte *)fi->request, strlen(fi->request), + FTPWrite, fi); + return; +} + +/* + * FTPDirRead + */ +static void +FTPDirRead(fi) +FTPInfo *fi; +{ + FTPReadData(fi, FTPDirData); + return; +} + +/* + * FTPNlst + */ +static void +FTPNlst(fi) +FTPInfo *fi; +{ + snprintf (fi->request, sizeof(fi->request), "NLST\r\n"); + FTPSimple(fi, FTPDirRead, false); + return; +} + +/* + * FTPDummy + */ +static void +FTPDummy(ios, len, closure) +ChimeraStream ios; +ssize_t len; +void *closure; +{ + return; +} + +/* + * FTPCwd + */ +static void +FTPCwd(fi) +FTPInfo *fi; +{ + int ecode; + char *filename; + + filename = fi->wr->up->filename; + + sscanf((char*)fi->cb, "%d", &ecode); + if (ecode < 400) + { + char *content; + + if ((content = ChimeraExt2Content(fi->cres, filename)) == NULL) + { + content = "text/plain"; + } + MIMEAddField(fi->mh, "content-type", content); + MIMEAddField(fi->mh, "x-url", fi->wr->url); + + SourceInit(fi->ws, fi->fp == NULL); + + FTPReadData(fi, FTPData); + FTPReadControl(fi, FTPDummy); + } + else + { + snprintf (fi->request, sizeof(fi->request), "CWD %s\r\n", filename); + FTPSimple(fi, FTPNlst, false); + } + + return; +} + +/* + * FTPRetrieve + */ +static void +FTPRetrieve(fi) +FTPInfo *fi; +{ + int h0, h1, h2, h3, p0, p1, reply, n; + const char *format = "RETR %s\r\n"; + char dhost[BUFSIZ]; + int dport; + + n = sscanf((char *)fi->cb, "%d %*[^(] (%d,%d,%d,%d,%d,%d)", + &reply, &h0, &h1, &h2, &h3, &p0, &p1); + if (n != 7 || reply != 227) + { + /* error */ + return; + } + + snprintf (dhost, sizeof(dhost), "%d.%d.%d.%d", h0, h1, h2, h3); + dport = (p0 << 8) + p1; + + /* + * Check for error here. + */ + if ((fi->ds = StreamCreateINet(fi->cres, dhost, dport)) == NULL) + { + return; + } + + snprintf (fi->request, sizeof(fi->request), format, fi->wr->up->filename); + FTPSimple(fi, FTPCwd, true); + + return; +} + +/* + * FTPPassive + */ +static void +FTPPassive(fi) +FTPInfo *fi; +{ + int ecode; + long size; + + sscanf((char *)fi->cb, "%d %ld", &ecode, &size); + + fi->dbmax = (size_t)size; + if (ecode >= 400) fi->dbmax = 0; + else + { + fi->db = (byte *)realloc_mem(fi->db, fi->dbmax); + fi->dbsize = fi->dbmax; + } + + snprintf (fi->request, sizeof(fi->request), "PASV\r\n"); + FTPSimple(fi, FTPRetrieve, true); + + return; +} + +/* + * FTPSize + */ +static void +FTPSize(fi) +FTPInfo *fi; +{ + snprintf (fi->request, sizeof(fi->request), + "SIZE %s\r\n", fi->wr->up->filename); + FTPSimple(fi, FTPPassive, true); + return; +} + +/* + * FTPType + */ +static void +FTPType(fi) +FTPInfo *fi; +{ + /* Now we know the password succeeded so check it as OK */ + if (fi->fp != NULL) fi->fp->good = true; + snprintf (fi->request, sizeof(fi->request), "TYPE I\r\n"); + FTPSimple(fi, FTPSize, false); + return; +} + +/* + * FTPPass + */ +static void +FTPPass(fi) +FTPInfo *fi; +{ + char *uname; + const char *pformat = "PASS %s\r\n"; + + if (fi->fp != NULL) + { + snprintf (fi->request, sizeof(fi->request), pformat, fi->fp->password); + } + else if (fi->wr->up->password != NULL) + { + snprintf (fi->request, sizeof(fi->request), pformat, fi->wr->up->password); + } + else if ((uname = getenv("EMAIL")) != NULL) + { + snprintf (fi->request, sizeof(fi->request), pformat, uname); + } + else + { + snprintf (fi->request, sizeof(fi->request), + "PASS -nobody@nowhere.org\r\n"); + } + FTPSimple(fi, FTPType, false); + + return; +} + +/* + * FTPAuthCallback + */ +static void +FTPAuthCallback(closure, username, password) +void *closure; +char *username; +char *password; +{ + FTPInfo *fi = (FTPInfo *)closure; + + if (username == NULL || password == NULL) + { + FTPCancel(fi); + return; + } + + FTPAddPassword(fi, username, password); + + AuthDestroy(fi->wa); + fi->wa = NULL; + + FTPUser(fi); + + return; +} + +/* + * FTPUser + */ +static void +FTPUser(fi) +FTPInfo *fi; +{ + const char *uformat = "USER %s\r\n"; + + if (fi->fp != NULL) + { + snprintf (fi->request, sizeof(fi->request), uformat, fi->fp->username); + } + else if (fi->wr->up->username != NULL) + { + if (fi->wr->up->password == NULL) + { + if ((fi->fp = FTPFindPassword(fi, fi->wr->up->username)) == NULL) + { + fi->wa = AuthCreate(fi->cres, "Enter password", fi->wr->up->username, + FTPAuthCallback, fi); + if (fi->wa != NULL) return; + } + } + + snprintf (fi->request, sizeof(fi->request), uformat, fi->wr->up->username); + } + else + { + snprintf (fi->request, sizeof(fi->request), "USER anonymous\r\n"); + } + + FTPSimple(fi, FTPPass, false); + + return; +} + +/* + * FTPInit + */ +static void * +FTPInit(ws, wr, class_closure) +ChimeraSource ws; +ChimeraRequest *wr; +void *class_closure; +{ + FTPInfo *fi; + MemPool mp; + size_t mlen, tlen; + int i; + + mp = MPCreate(); + fi = (FTPInfo *)MPCGet(mp, sizeof(FTPInfo)); + fi->mp = mp; + fi->ws = ws; + fi->wr = wr; + fi->cres = SourceToResources(ws); + fi->fc = (FTPClass *)class_closure; + fi->mh = MIMECreateHeader(); + + /* get the status messages and allocate a message work buffer */ + mlen = 0; + for (i = 0; ftp_messages[i].name != NULL; i++) + { + if ((fi->msg[i] = ResourceGetString(fi->cres, + ftp_messages[0].name)) == NULL) + { + fi->msg[i] = ftp_messages[i].def; + } + if ((tlen = strlen(fi->msg[i])) > mlen) mlen = tlen; + } + + snprintf (fi->msgbuf, sizeof(fi->msgbuf), + "%s %s", fi->msg[FM_OPEN], wr->up->hostname); + SourceSendMessage(fi->ws, fi->msgbuf); + + fi->cs = StreamCreateINet(fi->cres, + wr->up->hostname, + wr->up->port == 0 ? 21:wr->up->port); + if (fi->cs == NULL) + { + FTPDestroy(fi); + return(NULL); + } + + fi->rfunc = FTPUser; + FTPReadControl(fi, FTPSimpleRead); + + return(fi); +} + +/* + * FTPClassDestroy + */ +static void +FTPClassDestroy(closure) +void *closure; +{ + FTPClass *fc = (FTPClass *)closure; + MPDestroy(fc->mp); + return; +} + +/* + * FTPCancel + */ +static void +FTPCancel(closure) +void *closure; +{ + FTPInfo *fi = (FTPInfo *)closure; + + if (fi->wa != NULL) + { + AuthDestroy(fi->wa); + fi->wa = NULL; + } + FTPDestroyStream(fi); + + return; +} + +static void +FTPGetData(closure, data, len, mh) +void *closure; +byte **data; +size_t *len; +MIMEHeader *mh; +{ + FTPInfo *fi = (FTPInfo *)closure; + + *data = fi->db; + *len = fi->dblen; + *mh = fi->mh; + + return; +} + +/* + * InitModule_FTP + */ +void +InitModule_FTP(cres) +ChimeraResources cres; +{ + ChimeraSourceHooks ph; + FTPClass *fc; + MemPool mp; + + mp = MPCreate(); + fc = (FTPClass *)MPCGet(mp, sizeof(FTPClass)); + fc->mp = mp; + fc->passwords = GListCreateX(mp); + fc->header = ResourceGetString(cres, "ftp.dirheader"); + if (fc->header == NULL) + { + fc->header = "

    FTP Directory

      "; + } + + fc->trailer = ResourceGetString(cres, "ftp.dirtrailer"); + if (fc->trailer == NULL) fc->trailer = "
    "; + + memset(&ph, 0, sizeof(ph)); + ph.class_closure = fc; + ph.class_destroy = FTPClassDestroy; + ph.name = "ftp"; + ph.init = FTPInit; + ph.destroy = FTPDestroy; + ph.stop = FTPCancel; + ph.getdata = FTPGetData; + SourceAddHooks(cres, &ph); + + return; +} + +static int +ftp_strcmp(a, b) +const void *a, *b; +{ + return(strcmp(*((char **)a), *((char **)b))); +} + +/* + * FTPDirToHTML + * + * Convert directory list to HTML + */ +static char * +FTPDirToHTML(fi) +FTPInfo *fi; +{ + char *f; + int i; + char *hostname; + char *filename; + const char *entry = "
  • %s\n"; + byte *cp, *lastcp, *dname; + int sacount; + char **sa; + int olen, hlen, flen, elen; + FTPClass *fc = fi->fc; + + if (fi->wr->up->username != NULL) + { + size_t t; + + t = strlen(fi->wr->up->username); + t += strlen("@"); + t += strlen(fi->wr->up->hostname); + + hostname = MPGet(fi->mp, t + 1); + strcpy(hostname, fi->wr->up->username); + strcat(hostname, "@"); + strcat(hostname, fi->wr->up->hostname); + } + else + { + hostname = fi->wr->up->hostname; + } + + filename = fi->wr->up->filename; + hlen = strlen(hostname); + flen = strlen(filename); + elen = strlen(entry); + + sacount = 0; + for (cp = fi->db, lastcp = cp + fi->dblen; cp < lastcp; cp++) + { + if (*cp == '\n') sacount++; + } + sa = (char **)MPGet(fi->mp, sizeof(char *) * sacount); + dname = fi->db; + olen = 0; + for (i = 0, cp = fi->db, lastcp = cp + fi->dblen; cp < lastcp; cp++) + { + if (*cp == '\n') + { + sa[i] = (char *)dname; + *cp = '\0'; + dname = (byte *)cp + 1; + olen += hlen + 20 + flen + elen + strlen(sa[i]) * 2; + i++; + } + } + qsort(sa, sacount, sizeof(char *), ftp_strcmp); + + olen += strlen(fc->header) + 2 * flen + hlen + strlen(fc->trailer) + 1; + f = (char *)alloc_mem(olen); + snprintf (f, olen, fc->header, filename, hostname, filename); + + if (filename[0] != '\0' && filename[1] == '\0') filename = ""; + + for (i = 0; i < sacount; i++) + { + snprintf(f + strlen(f), olen - strlen(f), entry, + hostname, + fi->wr->up->port == 0 ? 21:fi->wr->up->port, + filename, + sa[i], sa[i]); + } + + strcat(f, fc->trailer); + + return(f); +} + diff --git a/proto/http.c b/proto/http.c new file mode 100644 index 0000000..bef8698 --- /dev/null +++ b/proto/http.c @@ -0,0 +1,1327 @@ +/* + * http.c + * + * Copyright (C) 1993-1997, John Kilburg + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +#include "port_before.h" + +#include + +#ifdef HAVE_STRING_H +#include +#endif + +#ifdef HAVE_STDLIB_H +#include +#endif + +#include "port_after.h" + +#include "Chimera.h" +#include "ChimeraStream.h" +#include "ChimeraAuth.h" +#include "ChimeraSource.h" + +#include "mime.h" + +/* + * OK, this is a little bit weird. The context that is passed is + * actually the address of the address of the context. This is + * because once a context is returned by HTTPInit it can't be + * changed (at least the way this circus works). Its convienent + * to be able to destroy the context when a new connection is made + * because of authorization stuff or a redirect occurs. + */ + +/* + * Size of chunks to allocate while reading. + */ +#define CHUNKSIZE 16384 + +#define PRINT_RATE 10 +#define MSGLEN 1024 + +#define HM_OPEN 0 +#define HM_RU 1 +#define HM_RK 2 +#define HM_DONE 3 +#define HM_SEND 4 +#define HM_WAITING 5 + +static struct http_message +{ + char *name; + char *def; +} http_messages[] = +{ + { "http.open", "Connecting to " }, + { "http.read_unknown", " bytes read so far." }, + { "http.read_known", " bytes remaining." }, + { "http.done", "HTTP read finished." }, + { "http.send", "Sending request..." }, + { "http.waiting", "Waiting for a response from " }, + { NULL, NULL }, +}; + +typedef struct +{ + char *username; + char *password; + char *hostname; + char *realm; + char *type; + int port; +} HTTPPassword; + +typedef struct +{ + MemPool mp; + char *msg[sizeof(http_messages) / sizeof(http_messages[0])]; + size_t mlen; + GList passwords; /* username/password/realm cache */ +} HTTPClass; + +typedef struct +{ + MemPool mp, rmp; + ChimeraSource ws; + ChimeraResources cres; + + /* addressing stuff */ + ChimeraRequest *wr; /* request addresses */ + char *url; /* URL to use for request */ + URLParts *up; /* URLParts to use for request */ + URLParts *pup; /* proxy server */ + + /* other stuff */ + HTTPClass *hc; + char msgbuf[MSGLEN]; + int nline; + int rcount; + + /* Information from HTTP status line */ + int status; + int major; + int minor; + + /* authorization stuff */ + ChimeraAuth wa; + HTTPPassword *hp; + char *auth_type; /* used for the Auth callback */ + char *realm; /* used for the Auth callback */ + + ChimeraStream ios; + + /* Information about the data received. */ + byte *b; + size_t blen; + size_t bsize; + size_t bmax; + size_t doff; /* offset to data beyond header */ + MIMEHeader mh; +} HTTPInfo; + +static int uuencode _ArgProto((unsigned char *, unsigned int, char *)); +static byte *HTTPBuildRequest _ArgProto((HTTPInfo *, ssize_t *)); +static int HTTPCreateInfo _ArgProto((HTTPInfo **, + ChimeraSource, ChimeraRequest *, + HTTPClass *, + URLParts *, URLParts *, + HTTPPassword *)); +static void HTTPCancel _ArgProto((void *)); +static void *HTTPInit _ArgProto((ChimeraSource, ChimeraRequest *, void *)); +static void HTTPDestroy _ArgProto((void *)); +static void HTTPDestroyInfo _ArgProto((HTTPInfo *)); +static void HTTPGetData _ArgProto((void *, byte **, size_t *, MIMEHeader *)); +static byte *HTTPRequest_Auth _ArgProto((HTTPInfo *, ssize_t *)); +static byte *HTTPRequest_UserAgent _ArgProto((HTTPInfo *, ssize_t *)); +static byte *HTTPRequest_AcceptLang _ArgProto((HTTPInfo *, ssize_t *)); +static byte *HTTPRequest_Accept _ArgProto((HTTPInfo *, ssize_t *)); +static byte *HTTPRequest_Pragma _ArgProto((HTTPInfo *, ssize_t *)); +static byte *HTTPRequest_Method _ArgProto((HTTPInfo *, ssize_t *)); +static byte *HTTPRequest_Host _ArgProto((HTTPInfo *, ssize_t *)); +static byte *HTTPRequest_Data1 _ArgProto((HTTPInfo *, ssize_t *)); +static byte *HTTPRequest_Data2 _ArgProto((HTTPInfo *, ssize_t *)); +static void HTTPFailure _ArgProto((HTTPInfo *)); +static void HTTPRead _ArgProto((HTTPInfo **, ChimeraStreamCallback)); +void HTTPReadHeader _ArgProto((ChimeraStream, ssize_t, void *)); +static void HTTPReadData _ArgProto((ChimeraStream, ssize_t, void *)); +static void HTTPReadUnknown _ArgProto((ChimeraStream, ssize_t, void *)); +static void HTTPRequestDone _ArgProto((ChimeraStream, ssize_t, void *)); +static void HTTPClassDestroy _ArgProto((void *)); +void InitModule_HTTP _ArgProto((ChimeraResources)); +static char *HTTPGetFilename _ArgProto((HTTPInfo *)); +static int HTTPCheckHeader _ArgProto((HTTPInfo **)); +HTTPPassword *HTTPFindPassword _ArgProto((HTTPInfo *, char *, + char *, int)); +HTTPPassword *HTTPAddPassword _ArgProto((HTTPInfo *, char *, char *, + char *, char *, char *, int)); +static void HTTPAuthCallback _ArgProto((void *, char *, char *)); + +/* + * crap for uuencode + */ +static char six2pr[64] = +{ + 'A','B','C','D','E','F','G','H','I','J','K','L','M', + 'N','O','P','Q','R','S','T','U','V','W','X','Y','Z', + 'a','b','c','d','e','f','g','h','i','j','k','l','m', + 'n','o','p','q','r','s','t','u','v','w','x','y','z', + '0','1','2','3','4','5','6','7','8','9','+','/' +}; + +/* + * uuencode + * + * I snarfed this code from some version of libwww. + * + * ACKNOWLEDGEMENT: + * This code is taken from rpem distribution, and was originally + * written by Mark Riordan. + * + * AUTHORS: + * MR Mark Riordan riordanmr@clvax1.cl.msu.edu + * AL Ari Luotonen luotonen@dxcern.cern.ch + * + */ +static int +uuencode(bufin, nbytes, bufcoded) +unsigned char *bufin; +unsigned int nbytes; +char *bufcoded; +{ + /* ENC is the basic 1 character encoding function to make a char printing */ +#define ENC(c) six2pr[c] + + register char *outptr = bufcoded; + unsigned int i; + + for (i=0; i> 2); /* c1 */ + *(outptr++) = ENC(((*bufin << 4) & 060) | ((bufin[1] >> 4) & 017)); /*c2*/ + *(outptr++) = ENC(((bufin[1] << 2) & 074) | ((bufin[2] >> 6) & 03));/*c3*/ + *(outptr++) = ENC(bufin[2] & 077); /* c4 */ + + bufin += 3; + } + + /* If nbytes was not a multiple of 3, then we have encoded too + * many characters. Adjust appropriately. + */ + if (i == nbytes + 1) + { + /* There were only 2 bytes in that last group */ + outptr[-1] = '='; + } + else if (i == nbytes + 2) + { + /* There was only 1 byte in that last group */ + outptr[-1] = '='; + outptr[-2] = '='; + } + *outptr = '\0'; + + return(outptr - bufcoded); +} + +/* + * HTTPAuthCallback + */ +static void +HTTPAuthCallback(closure, username, password) +void *closure; +char *username; +char *password; +{ + HTTPInfo **hip = (HTTPInfo **)closure; + HTTPInfo *hi = *hip; + HTTPPassword *hp; + + if (username == NULL || password == NULL) + { + HTTPCancel(hip); + return; + } + + hp = HTTPAddPassword(hi, username, password, hi->realm, hi->auth_type, + hi->up->hostname, hi->up->port); + + if (HTTPCreateInfo(hip, hi->ws, hi->wr, hi->hc, + hi->up, hi->pup, hp) == -1) + { + HTTPFailure(hi); + } + HTTPDestroyInfo(hi); + + return; +} + +/* + * HTTPFindPassword + */ +HTTPPassword * +HTTPFindPassword(hi, realm, hostname, port) +HTTPInfo *hi; +char *realm; +char *hostname; +int port; +{ + HTTPPassword *hp; + + port = port == 0 ? 80:port; + + for (hp = (HTTPPassword *)GListGetHead(hi->hc->passwords); hp != NULL; + hp = (HTTPPassword *)GListGetNext(hi->hc->passwords)) + { + if (strlen(realm) == strlen(hp->realm) && + strcmp(realm, hp->realm) == 0 && + strlen(hostname) == strlen(hp->hostname) && + strcasecmp(hostname, hp->hostname) == 0 && + port == hp->port) + { + return(hp); + } + } + + return(NULL); +} + +/* + * HTTPAddPassword + */ +HTTPPassword * +HTTPAddPassword(hi, username, password, realm, type, hostname, port) +HTTPInfo *hi; +char *username; +char *password; +char *realm; +char *type; +char *hostname; +int port; +{ + HTTPPassword *hp; + MemPool mp; + + if (username == NULL) return(NULL); + if (hostname == NULL) return(NULL); + + mp = hi->hc->mp; + + hp = (HTTPPassword *)MPCGet(mp, sizeof(HTTPPassword)); + hp->username = MPStrDup(mp, username); + if (password != NULL) hp->password = MPStrDup(mp, password); + else hp->password = MPStrDup(mp, ""); + + /* + * Probably shouldn't have defaults here. Should just error out. + */ + if (realm != NULL) hp->realm = MPStrDup(mp, realm); + else hp->realm = MPStrDup(mp, ""); + if (type != NULL) hp->type = MPStrDup(mp, type); + else hp->type = MPStrDup(mp, "basic"); + + hp->hostname = MPStrDup(mp, hostname); + hp->port = port == 0 ? 80:port; + GListAddHead(hi->hc->passwords, hp); + + return(hp); +} + +/* + * HTTPRequest_Auth + */ +static byte * +HTTPRequest_Auth(hi, len) +HTTPInfo *hi; +ssize_t *len; +{ + char *t, *line = NULL; + const char *authfield = "Authorization: "; + char *username; + char *password; + URLParts *up; + + if (hi->hp == NULL) return(NULL); + if (hi->hp->type == NULL || strcasecmp(hi->hp->type, "basic") != 0) + { + return(NULL); + } + + username = hi->hp->username; + password = hi->hp->password; + up = hi->up; + + line = (char *)MPGet(hi->rmp, + strlen(authfield) + + 2 * strlen(username) + 2 * strlen(":") + + 2 * strlen(password != NULL ? password:"") + + 2 * strlen(hi->hp->type) + + strlen("\r\n") + 1); + strcpy(line, username); + if (password != NULL) + { + strcat(line, ":"); + strcat(line, password); + } + + t = (char *)MPGet(hi->rmp, strlen(line) * 2); + uuencode(line, strlen(line), t); + + strcpy(line, authfield); + strcat(line, hi->hp->type); + strcat(line, " "); + strcat(line, t); + strcat(line, "\r\n"); + + if (line != NULL) *len = strlen(line); + + return((byte *)line); +} + +/* + * HTTPRequest_UserAgent + */ +static byte * +HTTPRequest_UserAgent(hi, len) +HTTPInfo *hi; +ssize_t *len; +{ + char *ua; + char *line = NULL; + const char *uaformat = "User-agent: %s\r\n"; + size_t linelen; + + ua = ResourceGetString(hi->cres, "http.userAgent"); + if (ua == NULL) return(NULL); + + linelen = strlen(uaformat) + strlen(ua) + 1; + line = (char *)MPGet(hi->rmp, linelen); + snprintf (line, linelen, uaformat, ua); + + if (line != NULL) *len = strlen(line); + + return((byte *)line); +} + +/* + * HTTPRequest_AcceptLang + */ +static byte * +HTTPRequest_AcceptLang(hi, len) +HTTPInfo *hi; +ssize_t *len; +{ + char *line; + char *acc; + const char *accformat = "Accept-Language: %s\r\n"; + size_t linelen; + + if ((acc = ResourceGetString(hi->cres, "http.acceptLanguage")) != NULL) + { + linelen = strlen(accformat) + strlen(acc) + 1; + line = (char *)MPGet(hi->rmp, linelen); + snprintf (line, linelen, accformat, acc); + *len = strlen(line); + return((byte *)line); + } + return(NULL); +} + +/* + * HTTPRequest_Accept + */ +static byte * +HTTPRequest_Accept(hi, len) +HTTPInfo *hi; +ssize_t *len; +{ + GList list; + char *nr; + size_t rlen; + size_t delimlen; + char *line = NULL; + const char *accfield = "Accept: "; + const char *delim = ","; + + if (hi->wr->contents == NULL) line = "Accept: */*\r\n"; + else + { + list = hi->wr->contents; + delimlen = strlen(delim); + rlen = 0; + for (nr = (char *)GListGetHead(list); nr != NULL; + nr = (char *)GListGetNext(list)) + { + rlen += strlen(nr) + delimlen; + } + + line = (char *)MPGet(hi->rmp, strlen(accfield) + + rlen + strlen("\r\n") + 1); + strcpy(line, accfield); + for (nr = (char *)GListGetHead(list); nr != NULL; ) + { + strcat(line, nr); + if ((nr = GListGetNext(list)) != NULL) strcat(line, delim); + } + strcat(line, "\r\n"); + } + + if (line != NULL) *len = strlen(line); + + return((byte *)line); +} + +/* + * HTTPRequest_Pragma + */ +static byte * +HTTPRequest_Pragma(hi, len) +HTTPInfo *hi; +ssize_t *len; +{ + const char *pragma = "Pragma: no-cache\r\n"; + + if (hi->wr->reload) + { + *len = strlen(pragma); + return((byte *)MPStrDup(hi->rmp, pragma)); + } + return(NULL); +} + +/* + * HTTPRequest_Method + */ +static byte * +HTTPRequest_Method(hi, len) +HTTPInfo *hi; +ssize_t *len; +{ + char *filename; + char *line = NULL; + ChimeraRequest *wr; + const char *getformat1 = "GET %s HTTP/1.0\r\n"; + const char *getformat2 = "GET %s%s%s HTTP/1.0\r\n"; + const char *postformat = "POST %s HTTP/1.0\r\n"; + size_t linelen; + + filename = HTTPGetFilename(hi); + wr = hi->wr; + + if (wr->input_method == NULL || strcasecmp(wr->input_method, "GET") == 0) + { + if (wr->input_data != NULL && wr->input_len > 0) + { + linelen = strlen(getformat2) + wr->input_len + strlen(filename) + + strlen("?") + 1; + + line = (char *)MPGet(hi->rmp, linelen); + snprintf (line, linelen, getformat2, filename, "?", wr->input_data); + } + else + { + linelen = strlen(getformat1) + strlen(filename) + 1; + line = (char *)MPGet(hi->rmp, linelen); + snprintf (line, linelen, getformat1, filename); + } + } + else if (strcasecmp(wr->input_method, "POST") == 0) + { + linelen = strlen(postformat) + strlen(filename) + 1; + line = (char *)MPGet(hi->rmp, linelen); + snprintf (line, linelen, postformat, filename); + } + else + { + *len = -1; + return(NULL); + } + + if (line != NULL) *len = strlen(line); + + return((byte *)line); +} + +/* + * HTTPRequest_Host + */ +static byte * +HTTPRequest_Host(hi, len) +HTTPInfo *hi; +ssize_t *len; +{ + char *host; + int port; + char *line; + size_t linelen; + + host = hi->up->hostname; + port = hi->up->port; + + linelen = strlen(host) + strlen("Host:") + 50; + line = (char *)MPCGet(hi->rmp, linelen); + if (port == 0) snprintf (line, linelen, "Host: %s\r\n", host); + else snprintf (line, linelen, "Host: %s:%d\r\n", host, port); + + if (line != NULL) *len = strlen(line); + + return((byte *)line); +} + +/* + * HTTPRequest_Data1 + */ +static byte * +HTTPRequest_Data1(hi, len) +HTTPInfo *hi; +ssize_t *len; +{ + char *line = NULL; + const char *format = "Content-type: %s\r\nContent-length: %d\r\n"; + ChimeraRequest *wr = hi->wr; + size_t linelen; + + if (wr->input_method == NULL || + strcasecmp(wr->input_method, "POST") != 0) + { + return(NULL); + } + + if (wr->input_data != NULL && wr->input_len > 0 && + wr->input_type != NULL) + { + linelen = strlen(format) + strlen(wr->input_type) + 101; + line = (char *)MPGet(hi->rmp, linelen); + snprintf (line, linelen, format, wr->input_type, wr->input_len); + } + + if (line != NULL) *len = strlen(line); + + return((byte *)line); +} + +/* + * HTTPRequest_Data2 + */ +static byte * +HTTPRequest_Data2(hi, len) +HTTPInfo *hi; +ssize_t *len; +{ + ChimeraRequest *wr = hi->wr; + + if (wr->input_method == NULL || + strcasecmp(wr->input_method, "POST") != 0) + { + return(NULL); + } + + if (wr->input_data != NULL && wr->input_len > 0 && + wr->input_type != NULL) + { + *len = wr->input_len; + return(wr->input_data); + } + + return(NULL); +} + +/* + * HTTPBuildRequest + * + * This became less efficient but easier to hack, I think. + */ +static byte * +HTTPBuildRequest(hi, len) +HTTPInfo *hi; +ssize_t *len; +{ + byte *line = NULL; + + do + { + *len = 0; + if (hi->nline == 0) line = HTTPRequest_Method(hi, len); + else if (hi->nline == 1) line = HTTPRequest_Host(hi, len); + else if (hi->nline == 2) line = HTTPRequest_UserAgent(hi, len); + else if (hi->nline == 3) line = HTTPRequest_Accept(hi, len); + else if (hi->nline == 4) line = HTTPRequest_AcceptLang(hi, len); + else if (hi->nline == 5) line = HTTPRequest_Pragma(hi, len); + else if (hi->nline == 6) line = HTTPRequest_Auth(hi, len); + /* this must be last */ + else if (hi->nline == 7) line = HTTPRequest_Data1(hi, len); + else if (hi->nline == 8) + { + *len = strlen("\r\n"); + line = (byte *)MPStrDup(hi->rmp, "\r\n"); + } + else if (hi->nline == 9) line = HTTPRequest_Data2(hi, len); + else break; + hi->nline++; + } while (line == NULL && *len != -1); + + return(line); +} + +/* + * HTTPFailure + */ +static void +HTTPFailure(hi) +HTTPInfo *hi; +{ + HTTPCancel(&hi); + SourceStop(hi->ws, "Read error during HTTP transfer"); + return; +} + +/* + * HTTPGetFilename + */ +static char * +HTTPGetFilename(hi) +HTTPInfo *hi; +{ + char *filename; + + /* + * If there is a proxy URL supplied then get the entire URL and not just + * just the filename part. If there is no proxy then just use the + * filename part. + */ + if (hi->pup != NULL) filename = URLMakeString(hi->mp, hi->up, false); + else filename = hi->up->filename; + if (filename == NULL) filename = "/"; + + return(filename); +} + +/* + * HTTPRead + */ +static void +HTTPRead(hip, func) +HTTPInfo **hip; +ChimeraStreamCallback func; +{ + size_t rsize; + HTTPInfo *hi = *hip; + + if (hi->bmax > 0) + { + rsize = hi->bmax - hi->blen; + if (rsize > CHUNKSIZE) rsize = CHUNKSIZE; + } + else rsize = CHUNKSIZE; + + if (hi->bmax == 0 || ((hi->bsize - hi->blen) < rsize)) + { + if (hi->b == NULL) hi->b = (byte *)alloc_mem(rsize); + else hi->b = (byte *)realloc_mem(hi->b, hi->bsize + rsize); + hi->bsize += rsize; + } + StreamRead(hi->ios, hi->b + hi->blen, rsize, func, hip); + return; +} + +/* + * HTTPReadData + */ +static void +HTTPReadData(ios, len, closure) +ChimeraStream ios; +ssize_t len; +void *closure; +{ + HTTPInfo **hip = (HTTPInfo **)closure; + HTTPInfo *hi = *hip; + long rnum; + char *rmsg; + + if (len < 0) + { + HTTPFailure(hi); + return; + } + + if (len > 0) hi->blen += len; + + if (len == 0 || ((hi->blen - hi->doff) == hi->bmax && hi->bmax > 0)) + { + SourceSendMessage(hi->ws, hi->hc->msg[HM_DONE]); + SourceEnd(hi->ws); + } + else + { + /* + * Don't want to return data until we know the final size...realloc() + * causes trouble. + */ + if (hi->bmax > 0) + { + if (hi->blen - hi->doff > 0) SourceAdd(hi->ws); + rmsg = hi->hc->msg[HM_RK]; + rnum = (long)(hi->bmax - hi->blen - hi->doff); + } + else + { + rmsg = hi->hc->msg[HM_RU]; + rnum = (long)(hi->blen - hi->doff); + } + + if (hi->rcount++ % PRINT_RATE == 0) + { + snprintf (hi->msgbuf, sizeof(hi->msgbuf), "%ld %s", rnum, rmsg); + SourceSendMessage(hi->ws, hi->msgbuf); + } + + HTTPRead(hip, HTTPReadData); + } + + return; +} + +/* + * HTTPCheckHeader + * + * Scrounges around in the header fields to see if there is any + * interesting information. + */ +static int +HTTPCheckHeader(hip) +HTTPInfo **hip; +{ + char *value; + size_t clen; + char *option; + char *list; + URLParts *up, *pup; + char *url; + char *username; + char *cp; + bool cache; + HTTPInfo *hi = *hip; + + if (hi->status >= 300) cache = false; + else cache = true; + + /* + * Take action on MIME fields. + */ + if ((MIMEGetField(hi->mh, "location", &value) == 0 && value != NULL) && + hi->status >= 300 && hi->status < 400) + { + up = URLParse(hi->mp, value); + pup = NULL; + + /* Just blindly follow the URL unless it should be used as a proxy */ + if (hi->status != 305) up = URLResolve(hi->mp, up, hi->up); + else + { + pup = up; + up = hi->up; + } + + if (HTTPCreateInfo(hip, hi->ws, hi->wr, hi->hc, + up, pup, hi->hp) == -1) + { + HTTPFailure(hi); + } + HTTPDestroyInfo(hi); + + return(-1); + } + if (MIMEGetField(hi->mh, "content-length", &value) == 0 && value != NULL) + { + clen = (size_t)atoi(value) + hi->doff; + if (clen > 0 && clen > hi->blen && clen > hi->bsize) + { + hi->b = (byte *)realloc_mem(hi->b, clen); + hi->bsize = clen; + hi->bmax = clen; + } + else hi->bmax = 0; + } + if (MIMEGetField(hi->mh, "pragma", &value) == 0 && value != NULL) + { + list = value; + while ((option = mystrtok(list, ' ', &list)) != NULL) + { + if (strcasecmp(option, "no-cache") == 0) cache = false; + } + } + if (MIMEGetField(hi->mh, "www-authenticate", &value) == 0 && + value != NULL && hi->hp == NULL) + { + cache = false; + + /* + * Look for the authorization type and realm + */ + list = value; + while ((option = mystrtok(list, ' ', &list)) != NULL) + { + if (strcasecmp(option, "basic") == 0) + { + hi->auth_type = MPStrDup(hi->mp, option); + } + else if (strncasecmp(option, "realm=", 6) == 0) + { + for (cp = option; *cp != '\0'; cp++) + { + if (*cp == '=') + { + hi->realm = MPStrDup(hi->mp, cp + 1); + break; + } + } + } + } + + /* + * If there is an authorization type and realm then try to + * see if the password is already known. If not then ask the user + * for a username and password. + */ + if (hi->auth_type != NULL && hi->realm != NULL) + { + if ((hi->hp = HTTPFindPassword(hi, hi->realm, + hi->up->hostname, + hi->up->port)) != NULL) + { + if (HTTPCreateInfo(hip, hi->ws, hi->wr, hi->hc, + hi->up, hi->pup, hi->hp) == -1) + { + HTTPFailure(hi); + } + HTTPDestroyInfo(hi); + return(-1); + } + else + { + if (hi->up->username != NULL) + { + if (hi->up->password != NULL) + { + HTTPAuthCallback(hip, hi->up->username, + hi->up->password); + } + else username = hi->up->username; + } + else username = ""; + + hi->wa = AuthCreate(hi->cres, "Enter password", username, + HTTPAuthCallback, hip); + + /* + * If the authorization context is created then return -1 to + * indicate that is the end of the transaction. If the context + * is not created then pass through so the auth message will + * appear. + */ + if (hi->wa != NULL) return(-1); + } + } + } + + if (MIMEGetField(hi->mh, "content-type", &value) != 0 || value == NULL) + { + if ((value = ChimeraExt2Content(hi->cres, HTTPGetFilename(hi))) == NULL) + { + value = "text/html"; + } + MIMEAddField(hi->mh, "content-type", value); + } + + if (hi->url != NULL) url = hi->url; + else url = "unknown:/"; + + MIMEAddField(hi->mh, "x-url", url); + + /* Do not call this before dealing with "Location:" */ + SourceInit(hi->ws, cache && hi->hp == NULL); + + return(0); +} + +/* + * HTTPReadHeader + */ +void +HTTPReadHeader(ios, len, closure) +ChimeraStream ios; +ssize_t len; +void *closure; +{ + HTTPInfo **hip = (HTTPInfo **)closure; + HTTPInfo *hi = *hip; + char *cp; + ssize_t i; + size_t moff = 0; + + if (len <= 0) + { + HTTPFailure(hi); + return; + } + + if (MIMEFindData(hi->mh, hi->b, hi->blen + len, &(hi->doff)) == 0) + { + /* + * Look for the end of the first line. + */ + for (i = 0, cp = (char *)hi->b; i < hi->doff; i++, cp++) + { + if (*cp == '\n') + { + moff = i + 1; + break; + } + } + + myassert(i < hi->doff, "MIMEFindData must be broken"); + + if (sscanf((char *)hi->b, "HTTP/%d.%d %d", + &hi->major, &hi->minor, &hi->status) != 3) + { + HTTPFailure(hi); + return; + } + + MIMEParseBuffer(hi->mh, hi->b + moff, hi->doff - moff); + + if (HTTPCheckHeader(hip) == -1) return; + + HTTPReadData(ios, len, closure); + } + else + { + snprintf (hi->msgbuf, sizeof(hi->msgbuf), + "%ld %s", (long)hi->blen, hi->hc->msg[HM_RU]); + SourceSendMessage(hi->ws, hi->msgbuf); + + hi->blen += len; + + HTTPRead(hip, HTTPReadHeader); + } + + return; +} + +/* + * HTTPReadUnknown + */ +static void +HTTPReadUnknown(ios, len, closure) +ChimeraStream ios; +ssize_t len; +void *closure; +{ + HTTPInfo **hip = (HTTPInfo **)closure; + HTTPInfo *hi = *hip; + char *content; + size_t nblen; + + if (len < 0 || (len == 0 && hi->blen == 0)) + { + HTTPFailure(hi); + return; + } + + nblen = hi->blen + len; + + if (nblen < 5 && len > 0) + { + hi->blen = nblen; + + /* Not enough information to know the HTTP version */ + HTTPRead(hip, HTTPReadUnknown); + } + else if (nblen >= 5 && strncmp("HTTP/", (char *)hi->b, 5) == 0) + { + HTTPReadHeader(ios, len, hip); + } + else + { + /* Must be HTTP/0.9 */ + if ((content = ChimeraExt2Content(hi->cres, HTTPGetFilename(hi))) == NULL) + { + content = "text/html"; + } + + MIMEAddField(hi->mh, "content-type", content); + MIMEAddField(hi->mh, "x-url", hi->url); + + SourceInit(hi->ws, true); + + HTTPReadData(ios, len, hip); + } + + snprintf (hi->msgbuf, sizeof(hi->msgbuf), + "%ld %s", (long)hi->blen, hi->hc->msg[HM_RU]); + SourceSendMessage(hi->ws, hi->msgbuf); + + return; +} + +/* + * HTTPRequestDone + */ +static void +HTTPRequestDone(ios, rval, closure) +ChimeraStream ios; +ssize_t rval; +void *closure; +{ + byte *rdata; + ssize_t rlen; + HTTPInfo **hip = (HTTPInfo **)closure; + HTTPInfo *hi = *hip; + + if (rval == -1) + { + HTTPFailure(hi); + return; + } + + if ((rdata = HTTPBuildRequest(hi, &rlen)) == NULL) + { + if (rlen == -1) HTTPFailure(hi); + else HTTPRead(hip, HTTPReadUnknown); + } + else + { + StreamWrite(hi->ios, rdata, rlen, HTTPRequestDone, hip); + } + + return; +} + +/* + * HTTPDestroy + */ +static void +HTTPDestroy(closure) +void *closure; +{ + HTTPInfo **hip = (HTTPInfo **)closure; + + HTTPDestroyInfo(*hip); + free_mem(hip); + + return; +} + +/* + * HTTPDestroyInfo + */ +static void +HTTPDestroyInfo(hi) +HTTPInfo *hi; +{ + HTTPCancel(&hi); + if (hi->rmp != NULL) MPDestroy(hi->rmp); + if (hi->b != NULL) free_mem(hi->b); + if (hi->mh != NULL) MIMEDestroyHeader(hi->mh); + MPDestroy(hi->mp); + + return; +} + +/* + * HTTPCreateInfo + */ +static int +HTTPCreateInfo(hip, ws, wr, hc, up, pup, hp) +HTTPInfo **hip; +ChimeraSource ws; +ChimeraRequest *wr; +HTTPClass *hc; +URLParts *up, *pup; +HTTPPassword *hp; +{ + char *hostname; + int port; + HTTPInfo *hi; + MemPool mp; + + mp = MPCreate(); + *hip = hi = (HTTPInfo *)MPCGet(mp, sizeof(HTTPInfo)); + hi->mp = mp; + hi->hp = hp; + hi->rmp = MPCreate(); + hi->ws = ws; + hi->wr = wr; + hi->cres = SourceToResources(ws); + hi->hc = hc; + hi->mh = MIMECreateHeader(); + hi->up = URLDup(mp, up); + hi->url = URLMakeString(hi->mp, hi->up, true); + if (pup != NULL) hi->pup = URLDup(mp, pup); + else hi->pup = NULL; + + if (hi->pup != NULL) + { + hostname = pup->hostname; + port = pup->port; + } + else + { + hostname = up->hostname; + port = up->port; + } + + snprintf (hi->msgbuf, sizeof(hi->msgbuf), + "%s %s", hc->msg[HM_OPEN], hostname); + SourceSendMessage(hi->ws, hi->msgbuf); + + hi->ios = StreamCreateINet(hi->cres, hostname, port == 0 ? 80:port); + if (hi->ios == NULL) + { + HTTPDestroyInfo(hi); + return(-1); + } + + HTTPRequestDone(hi->ios, 0, (void *)hip); + + return(0); +} + +/* + * HTTPInit + */ +static void * +HTTPInit(ws, wr, class_closure) +ChimeraSource ws; +ChimeraRequest *wr; +void *class_closure; +{ + HTTPClass *hc = (HTTPClass *)class_closure; + HTTPInfo **hip; + + hip = (HTTPInfo **)alloc_mem(sizeof(HTTPInfo **)); + + if (HTTPCreateInfo(hip, ws, wr, hc, wr->up, wr->pup, NULL) == -1) + { + free_mem(hip); + return(NULL); + } + + return(hip); +} + +/* + * HTTPCancel + * + * Used to obliterate a connection. Use this when an error occurs which + * could cause a connection to get messed up or at least a pain to + * figure out. + */ +void +HTTPCancel(closure) +void *closure; +{ + HTTPInfo *hi = *((HTTPInfo **)closure); + + if (hi->wa != NULL) + { + AuthDestroy(hi->wa); + hi->wa = NULL; + } + + if (hi->ios != NULL) + { + StreamDestroy(hi->ios); + hi->ios = NULL; + } + + return; +} + +/* + * HTTPClassDestroy + */ +static void +HTTPClassDestroy(closure) +void *closure; +{ + HTTPClass *hc = (HTTPClass *)closure; + MPDestroy(hc->mp); + return; +} + +/* + * HTTPGetData + */ +static void +HTTPGetData(closure, data, len, mh) +void *closure; +byte **data; +size_t *len; +MIMEHeader *mh; +{ + HTTPInfo *hi = *((HTTPInfo **)closure); + + *data = hi->b + hi->doff; + *len = hi->blen - hi->doff; + *mh = hi->mh; + + return; +} + + +/* + * InitModule_HTTP + */ +void +InitModule_HTTP(cres) +ChimeraResources cres; +{ + ChimeraSourceHooks ph; + HTTPClass *hc; + MemPool mp; + size_t tlen; + int i; + + mp = MPCreate(); + hc = (HTTPClass *)MPCGet(mp, sizeof(HTTPClass)); + hc->mp = mp; + hc->passwords = GListCreateX(mp); + + /* get the status messages and allocate a message work buffer */ + for (i = 0; http_messages[i].name != NULL; i++) + { + if ((hc->msg[i] = ResourceGetString(cres, http_messages[0].name)) == NULL) + { + hc->msg[i] = http_messages[i].def; + } + if ((tlen = strlen(hc->msg[i])) > hc->mlen) hc->mlen = tlen; + } + + memset(&ph, 0, sizeof(ph)); + ph.class_closure = hc; + ph.name = "http"; + ph.init = HTTPInit; + ph.destroy = HTTPDestroy; + ph.stop = HTTPCancel; + ph.getdata = HTTPGetData; + ph.class_destroy = HTTPClassDestroy; + SourceAddHooks(cres, &ph); + + return; +} diff --git a/proto/mailto.c b/proto/mailto.c new file mode 100644 index 0000000..8cabe1f --- /dev/null +++ b/proto/mailto.c @@ -0,0 +1,504 @@ +/* + * mailto.c + * + * Copyright 1997, Dave Davey + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. + */ +#include "port_before.h" + +#include + +#ifdef HAVE_STDLIB_H +#include +#endif + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef HAVE_UNAME +#include +#endif + +#include "port_after.h" + +#include "../mxw/TextField.h" + +#include "common.h" + +#include "../mxw/MyDialog.h" + +#include "Chimera.h" +#include "ChimeraSource.h" + +#include "mime.h" + + +/* + * hacked by john a little bit so you know where the blames goes. + * status messages are ugly. + */ + +typedef struct MailtoInfoP MailtoInfo; +typedef struct MailtoActionInfoP MailtoActionInfo; + +char *URLUnescape(MemPool mp, char *url); + +static char *sendmail _ArgProto((ChimeraResources, char *)); + +void InitModule_Mailto _ArgProto((ChimeraResources)); +static void MailtoClassDestroy _ArgProto((void *)); +static void *MailtoInit _ArgProto((ChimeraSource, ChimeraRequest *, void *)); +static void MailtoCancel _ArgProto((void *)); +static void MailtoDestroy _ArgProto((void *)); +static void MailtoGetData _ArgProto((void *, byte **, size_t *, MIMEHeader *)); +static void MakeMailtoForm _ArgProto((void *)); + +static void *MailtoActionInit _ArgProto((ChimeraSource, + ChimeraRequest *, void *)); +static void MailtoActionCancel _ArgProto((void *)); +static void MailtoActionDestroy _ArgProto((void *)); +static void MailtoActionGetData _ArgProto((void *, + byte **, size_t *, MIMEHeader *)); + +typedef struct +{ + MemPool mp; + char *header; + char *trailer; +} MailtoClass; + +struct MailtoInfoP +{ + MemPool mp; + ChimeraSource ws; + ChimeraResources cres; + ChimeraRequest *wr; + MailtoClass *mc; + ChimeraTask wt; + byte *data; + size_t len; + byte *address; + MIMEHeader mh; +}; + +struct MailtoActionInfoP +{ + MemPool mp; + ChimeraSource ws; + ChimeraResources cres; + ChimeraRequest *wr; + ChimeraTask wt; + byte *data; + size_t len; + MIMEHeader mh; +}; + +/* + * InitModule_Mailto + */ +void +InitModule_Mailto(cres) +ChimeraResources cres; +{ + ChimeraSourceHooks ph; + MailtoClass *mc; + MemPool mp; + + mp = MPCreate(); + mc = (MailtoClass *)MPCGet(mp, sizeof(MailtoClass)); + mc->mp = mp; + mc->header = ResourceGetString(cres, "mailto.dirheader"); + if (mc->header == NULL) + { + mc->header = "

    Mailto

      "; + } + + mc->trailer = ResourceGetString(cres, "mailto.dirtrailer"); + if (mc->trailer == NULL) mc->trailer = ""; + + memset(&ph, 0, sizeof(ph)); + ph.class_closure = mc; + ph.class_destroy = MailtoClassDestroy; + ph.name = "mailto"; + ph.init = MailtoInit; + ph.destroy = MailtoDestroy; + ph.stop = MailtoCancel; + ph.getdata = MailtoGetData; + SourceAddHooks(cres, &ph); + + memset(&ph, 0, sizeof(ph)); + ph.name = "x-mailtoaction"; + ph.init = MailtoActionInit; + ph.destroy = MailtoActionDestroy; + ph.stop = MailtoActionCancel; + ph.getdata = MailtoActionGetData; + SourceAddHooks(cres, &ph); + + return; +} + +/* + * MailtoClassDestroy + */ +static void +MailtoClassDestroy(closure) +void *closure; +{ + MailtoClass *mc = (MailtoClass *)closure; + MPDestroy(mc->mp); + return; +} + +/* + * MailtoInit + */ +static void * +MailtoInit(ws, wr, class_closure) +ChimeraSource ws; +ChimeraRequest *wr; +void *class_closure; +{ + /* + ** get here when click on mailto link + ** wr->url points to url including "mailto" + */ + MailtoInfo *mi; + MemPool mp; + ChimeraTaskProc func; + char *p; + + if((p = strchr(wr->url, ':')) == NULL) + return(NULL); + mp = MPCreate(); + mi = (MailtoInfo *)MPCGet(mp, sizeof(MailtoInfo)); + mi->address = p+1; + mi->mp = mp; + mi->ws = ws; + mi->wr = wr; + mi->cres = SourceToResources(ws); + + mi->mc = (MailtoClass *)class_closure; + func = MakeMailtoForm; + mi->wt = TaskSchedule(mi->cres, func, mi); + mi->mh = MIMECreateHeader(); + MIMEAddField(mi->mh, "content-type", "text/html"); + MIMEAddField(mi->mh, "x-url", mi->wr->url); + return(mi); +} + +/* + * MailtoCancel + */ +static void +MailtoCancel(closure) +void *closure; +{ + /* nothing to do here ? */ + /* what if sendmail "hangs" ? */ + return; +} + +/* + * MailtoDestroy + */ +static void +MailtoDestroy(closure) +void *closure; +{ + MailtoInfo *mi = (MailtoInfo *)closure; + if (mi->wt != NULL) TaskRemove(mi->cres, mi->wt); + MPDestroy(mi->mp); + return; +} + +static void +MailtoGetData(closure, data, len, mh) +void *closure; +byte **data; +size_t *len; +MIMEHeader *mh; +{ + MailtoInfo *mi = (MailtoInfo *)closure; + + *data = mi->data; + *len = mi->len; + *mh = mi->mh; + + return; +} + +enum +{ + FROM, + TO, + CC, + SUBJECT, + MESSAGE +}; +struct MailHeader +{ + char *name; + char *contents; + char *input_type; +} MailHeaders[] = +{ + { "From", "", "INPUT size=64" }, + { "To", "", "INPUT size=64" }, + { "Cc", "", "INPUT size=64" }, + { "Subject", "", "INPUT size=64" }, + { "Message", "", "TEXTAREA rows=20 cols=64" }, + { NULL, NULL } +}; +/* + * MakeMailForm + */ +static void +MakeMailtoForm(closure) +void *closure; +{ + char *f; + MailtoInfo *mi = (MailtoInfo *)closure; + struct passwd *p; +#ifdef HAVE_UNAME + struct utsname uts; +#endif + char *sender; + char *username; + struct MailHeader *mh; + + if ((sender = ResourceGetString(mi->cres, "user.email")) == NULL) + { +#ifdef HAVE_UNAME + + if((p = getpwuid(getuid())) == NULL) username = "nobody"; + else username = p->pw_name; + + uname(&uts); + sender = (char *)MPCGet(mi->mp, strlen(username) + + strlen(uts.nodename) + 2); + strcpy(sender, username); + strcat(sender, "@"); + strcat(sender, uts.nodename); +#else + sender = ""; +#endif + } + + MailHeaders[FROM].contents = sender; + MailHeaders[TO].contents = mi->address; + MailHeaders[CC].contents = ""; + MailHeaders[SUBJECT].contents = mi->wr->parent_url; + MailHeaders[MESSAGE].contents = ""; + + f = (char *)MPCGet(mi->mp, BUFSIZ); + strcpy(f, "\n\nChimera Mail\n\n"); + strcat(f, "\n

      Chimera Mail

      \n
      \n"); + strcat(f, "
      \n\n"); + + for(mh = MailHeaders; mh->name != NULL; mh++) + { + strcat(f, "\n"); + } + + strcat(f, "
      \n

      "); + strcat(f, mh->name); + strcat(f, ":

      \n<"); + strcat(f, mh->input_type); + strcat(f, " name=\""); + strcat(f, mh->name); + strcat(f, "\" type=\"text\" value=\""); + if(mh->contents) + strcat(f, mh->contents); + strcat(f, "\">\n"); + strcat(f, "
      \n"); + + strcat(f, "\n"); + strcat(f, "\n"); + strcat(f, "
      \n\n\n"); + + mi->data = (byte *)f; + mi->len = strlen(f); + SourceInit(mi->ws, false); + SourceEnd(mi->ws); + + mi->wt = NULL; + return; +} + +static void +MailtoAction(closure) +void *closure; +{ + MailtoActionInfo *mi = (MailtoActionInfo *)closure; + char *f; + + if (strstr(mi->wr->input_data, "Cancel") != NULL) + { + f = "mail cancelled by the user"; + } + else if (strstr(mi->wr->input_data, "SendMail") != NULL) + { + f = sendmail(mi->cres, mi->wr->input_data); + } + else f = "unexpected input_data"; + + mi->data = (byte *)f; + mi->len = strlen(f); + SourceInit(mi->ws, false); + SourceEnd(mi->ws); + + mi->wt = NULL; + + return; +} + +static char * +sendmail(cres, data) +ChimeraResources cres; +char *data; +{ + MemPool mp; + FILE *fp; + char *p; + struct MailHeader *mh; + char *mailer; + char *f; + + if ((mailer = ResourceGetString(cres, "mailto.mailer")) == NULL) + { +#ifdef __EMX__ + mailer = "sendmail -t"; +#else + mailer = "/usr/lib/sendmail -t"; +#endif + } + + if ((fp = popen(mailer, "w")) != NULL) + { + mp = MPCreate(); + mh = MailHeaders; + + for(mh = MailHeaders; mh->name != NULL; mh++) + { + p = strstr(data, mh->name) + strlen(mh->name) + 1; + data = strchr(data, '&'); + *data++ = '\0'; + mh->contents = URLUnescape(mp, p); + if(strcmp(mh->name, "Message") == 0) + fprintf(fp, "X-Mailer: Chimera\n\n"); + else + { + if(mh->contents == NULL || strlen(mh->contents) == 0) + continue; + fprintf(fp,"%s: ", mh->name); + } + fprintf(fp, "%s\n", mh->contents); + } + /* signature ?? */ + pclose(fp); + MPDestroy(mp); + + f = "mail sent"; + } + else f = "popen() failed"; + + return(f); +} + +/* + * MailtoActionInit + */ +static void * +MailtoActionInit(ws, wr, class_closure) +ChimeraSource ws; +ChimeraRequest *wr; +void *class_closure; +{ + /* + ** get here when click on mailto link + ** wr->url points to url including "x-mailtoaction" + */ + MailtoActionInfo *mi; + MemPool mp; + ChimeraTaskProc func; + + mp = MPCreate(); + mi = (MailtoActionInfo *)MPCGet(mp, sizeof(MailtoActionInfo)); + mi->mp = mp; + mi->ws = ws; + mi->wr = wr; + mi->cres = SourceToResources(ws); + + func = MailtoAction; + mi->wt = TaskSchedule(mi->cres, func, mi); + mi->mh = MIMECreateHeader(); + MIMEAddField(mi->mh, "content-type", "text/plain"); + MIMEAddField(mi->mh, "x-url", mi->wr->url); + + return(mi); +} + +/* + * MailtoActionCancel + */ +static void +MailtoActionCancel(closure) +void *closure; +{ + /* nothing to do here ? */ + /* what if sendmail "hangs" ? */ + return; +} + +/* + * MailtoActionDestroy + */ +static void +MailtoActionDestroy(closure) +void *closure; +{ + MailtoActionInfo *mi = (MailtoActionInfo *)closure; + if (mi->wt != NULL) TaskRemove(mi->cres, mi->wt); + MPDestroy(mi->mp); + return; +} + +static void +MailtoActionGetData(closure, data, len, mh) +void *closure; +byte **data; +size_t *len; +MIMEHeader *mh; +{ + MailtoActionInfo *mi = (MailtoActionInfo *)closure; + + *data = mi->data; + *len = mi->len; + *mh = mi->mh; + + return; +}