Ejemplo n.º 1
0
static void
ginit(void)
{
	static int first = 1;
	int i;
	
	if(!first)
		return;
		
	first = 0;
	memimageinit();
#ifdef PLAN9PORT
	smallfont = openmemsubfont(unsharp("#9/font/lucsans/lstr.10"));
#else
	smallfont = openmemsubfont("/lib/font/bit/lucidasans/lstr.10");
#endif
	black = memblack;
	blue = allocrepl(DBlue);
	red = allocrepl(DRed);
	grid = allocrepl(0x77777777);
	for(i=0; i<nelem(fill)/2 && i<nelem(lofill) && i<nelem(hifill); i++){
		lofill[i] = allocrepl(fill[2*i]);
		hifill[i] = allocrepl(fill[2*i+1]);
	}
}
Ejemplo n.º 2
0
/*
 * get command line flags, initialize keywords & traps.
 * get values from environment.
 * set $pid, $cflag, $*
 * fabricate bootstrap code and start it (*=(argv);. /usr/lib/rcmain $*)
 * start interpreting code
 */
int
main(int argc, char *argv[])
{
	code bootstrap[32];
	char num[12], *rcmain;
	int i;
	
	/* needed for rcmain later */
	putenv("PLAN9", unsharp("#9"));
	argc = getflags(argc, argv, "ftjSsrdiIlxepvVc:1m:1[command]", 1);
	if(argc==-1)
		usage("[file [arg ...]]");
	if(argv[0][0]=='-')
		flag['l'] = flagset;
	if(flag['I'])
		flag['i'] = 0;
	else if(flag['i']==0 && argc==1 && Isatty(0)) flag['i'] = flagset;
	rcmain = flag['m'] ? flag['m'][0] : Rcmain();
	err = openfd(2);
	kinit();
	Trapinit();
	Vinit();
	inttoascii(num, mypid = getpid());
	pathinit();
	setvar("pid", newword(num, (word *)0));
	setvar("cflag", flag['c']?newword(flag['c'][0], (word *)0)
				:(word *)0);
	setvar("rcname", newword(argv[0], (word *)0));
	i = 0;
	bootstrap[i++].i = 1;
	bootstrap[i++].f = Xmark;
	bootstrap[i++].f = Xword;
	bootstrap[i++].s="*";
	bootstrap[i++].f = Xassign;
	bootstrap[i++].f = Xmark;
	bootstrap[i++].f = Xmark;
	bootstrap[i++].f = Xword;
	bootstrap[i++].s="*";
	bootstrap[i++].f = Xdol;
	bootstrap[i++].f = Xword;
	bootstrap[i++].s = rcmain;
	bootstrap[i++].f = Xword;
	bootstrap[i++].s=".";
	bootstrap[i++].f = Xsimple;
	bootstrap[i++].f = Xexit;
	bootstrap[i].i = 0;
	start(bootstrap, 1, (var *)0);
	/* prime bootstrap argv */
	pushlist();
	argv0 = strdup(argv[0]);
	for(i = argc-1;i!=0;--i) pushword(argv[i]);
	for(;;){
		if(flag['r'])
			pfnc(err, runq);
		runq->pc++;
		(*runq->code[runq->pc-1].f)();
		if(ntrap)
			dotrap();
	}
}
Ejemplo n.º 3
0
void
main(int argc, char *argv[])
{
	int i;

	NEWS = unsharp(NEWS);

	Binit(&bout, 1, OWRITE);
	if(argc == 1) {
		eachitem(print_item, 0, 1);
		exits(0);
	}
	ARGBEGIN{
	case 'a':	/* print all */
		eachitem(print_item, 1, 0);
		break;

	case 'n':	/* names only */
		eachitem(note, 0, 0);
		if(n_items)
			Bputc(&bout, '\n');
		break;

	default:
		fprint(2, "news: bad option %c\n", ARGC());
		exits("usage");
	}ARGEND
	for(i=0; i<argc; i++)
		print_item(argv[i]);
	exits(0);
}
Ejemplo n.º 4
0
void
main(int argc, char **argv)
{
	int i, dostdin;
	char *p;
	Rune *r;
	Rune buf[2];
	
	Binit(&bout, 1, OWRITE);
	fmtinstall('L', linefmt);
	quotefmtinstall();
	
	tmacdir = unsharp("#9/tmac");
	dostdin = 0;
	ARGBEGIN{
	case 'i':
		dostdin = 1;
		break;
	case 'm':
		r = erunesmprint("%s/tmac.%s", tmacdir, EARGF(usage()));
		if(queueinputfile(r) < 0)
			fprint(2, "%S: %r\n", r);
		break;
	case 'r':
		p = EARGF(usage());
		p += chartorune(buf, p);
		buf[1] = 0;
		_nr(buf, erunesmprint("%s", p+1));
		break;
	case 'u':
		utf8 = 1;
		break;
	case 'v':
		verbose = 1;
		break;
	default:
		usage();
	}ARGEND

	for(i=0; i<argc; i++){
		if(strcmp(argv[i], "-") == 0)
			queuestdin();
		else
			queueinputfile(erunesmprint("%s", argv[i]));
	}
	if(argc == 0 || dostdin)
		queuestdin();
	
	run();
	Bprint(&bout, "\n");
	Bterm(&bout);
	exits(nil);
}
Ejemplo n.º 5
0
int
main(int argc, char *argv[])
{
	extern void onintr(int), fpecatch(int);

	lib_defines = unsharp(GRAPDEFINES);

	if (signal(SIGINT, SIG_IGN) != SIG_IGN)
		signal(SIGINT, onintr);
	signal(SIGFPE, fpecatch);
	cmdname = argv[0];
	tempfile = strdup("grap.XXXXXX");
	mkstemp(tempfile);
	while (argc > 1 && *argv[1] == '-') {
		switch (argv[1][1]) {
		case 'd':
			dbg = 1;
			tfd = stdout;
			strcpy(tempfile, "grap.temp");
			unlink(tempfile);
			fprintf(stderr, "%s\n", version);
			break;
		case 'l':	/* turn off /usr/lib inclusion */
			lib = 0;
			break;
		}
		argc--;
		argv++;
	}
	setdefaults();
	curfile = infile;
	if (argc <= 1) {
		curfile->fin = stdin;
		curfile->fname = tostring("-");
		pushsrc(File, curfile->fname);
		getdata();
	} else
		while (argc-- > 1) {
			if ((curfile->fin = fopen(*++argv, "r")) == NULL) {
				fprintf(stderr, "grap: can't open %s\n", *argv);
				onintr(0);
			}
			curfile->fname = tostring(*argv);
			pushsrc(File, curfile->fname);
			getdata();
			fclose(curfile->fin);
			free(curfile->fname);
		}
	if (!dbg)
		unlink(tempfile);
	exit(0);
}
Ejemplo n.º 6
0
void
threadmain(int argc, char *argv[])
{
	char buf[512];
	int fd;

	progname = "plumber";

	ARGBEGIN{
	case 'd':
		debug = 1;
		break;
	case 'p':
		plumbfile = ARGF();
		break;
	}ARGEND

	user = getuser();
	home = getenv("HOME");
	if(user==nil || home==nil)
		error("can't initialize $user or $home: %r");
	if(plumbfile == nil){
		sprint(buf, "%s/lib/plumbing", home);
		if(access(buf, 0) >= 0)
			plumbfile = estrdup(buf);
		else
			plumbfile = unsharp("#9/plumb/initial.plumbing");
	}

	fd = open(plumbfile, OREAD);
	if(fd < 0)
		error("can't open rules file %s: %r", plumbfile);
	if(setjmp(parsejmp))
		error("parse error");

	rules = readrules(plumbfile, fd);
	close(fd);

	/*
	 * Start all processes and threads from other proc
	 * so we (main pid) can return to user.
	 */
	printerrors = 0;
	makeports(rules);
	startfsys();
	threadexits(nil);
}
Ejemplo n.º 7
0
/* Movement of the paddles that is correlated in space and in time
 * This method currently operates on a time-schedule of 1 new correlation computation to the grid every 0.1 seconds.
 * In principle, this can be easily modified to separate the time-scale of the grid from the time-scale of the correlations
 *  (e.g. by running one correlation every 0.5 seconds but still sending angles to the grid every 0.1 seconds). The current schedule
 *  gives the highest rate of correlation computations we can handle, given the nature of the runcorr_3D algorithm and our current computers.
 * We define the term "constraining" as the occasion when a paddle is given a new angle to move to that it cannot reach without exceeding the
 *  limits of its servo (e.g. an amplitude of 60 degrees when the maximum angular distance it can travel in 0.1 seconds is 42.8 degrees). Such
 *  noncompliant paddles will be "constrained" to move at the greatest allowed servo speed (e.g. 4.28 deg/s instead of 6 deg/s) and thus will
 *  not reach their target destinations. This was a necessary comprimise in order to maximize the grid's correlation computation rate.
 */
int correlatedMovement_correlatedInTime(int constantArea, float spatial_sigma, float temporal_sigma, float spatial_alpha, float temporal_alpha, float spatial_height, float temporal_height, int typeOfSpatialCorr, int typeOfTemporalCorr, float target_rms, int numberOfSlices){

    /* To install mySignalHandler (which prints statistics when you type CTRL-C)
    you must make sure that CTRL-C signals are not blocked.
    If you are having problems with the signal handler (i.e. CTRL-C signals are blocked)
    you can just comment out the following block and the block after this one where the signal handler is actually installed.*/
    void (*pfRet)(int);
    sigset_t sSet;
    int iRet;
    iRet = sigemptyset(&sSet);
    if (iRet == -1) {perror("ERROR installing signal handler in correlatedMovement_correlatedInTime"); exit(EXIT_FAILURE); }
    iRet = sigaddset(&sSet, SIGINT);
    if (iRet == -1) {perror("ERROR installing signal handler in correlatedMovement_correlatedInTime"); exit(EXIT_FAILURE); }
    iRet = sigprocmask(SIG_UNBLOCK, &sSet, NULL);
    if (iRet == -1) {perror("ERROR installing signal handler in correlatedMovement_correlatedInTime"); exit(EXIT_FAILURE); }
    
    // Install mySignalHandler as the handler for CTRL-C signals.
    pfRet = signal(SIGINT, mySignalHandler);
    if (pfRet == SIG_ERR) {perror("error installing signal handler in correlatedMovement_correlatedInTime"); exit(EXIT_FAILURE); }

    // special method selection (for slow or unconventional kernels that need to be inlined)
    bool ltfast_on = false;
    bool unsharp_on = false;
    if (typeOfSpatialCorr == 8 && typeOfTemporalCorr == 8) {
        cout << "\n***********************************\n";
        cout << "*      Running ltfast method      *";
        cout << "\n***********************************" << endl;
        ltfast_on = true;
    }
    if (typeOfSpatialCorr == 10 && typeOfTemporalCorr == 10) {
        cout << "\n************************************\n";
        cout << "*      Running double unsharp      *";
        cout << "\n************************************" << endl;
        unsharp_on = true;
    }
    // NOTE: if you're interested, you could re-implement true top hats by inlining them here as well
    
    // UI to avoid accidentally overwriting angle files
    ifstream ifile("angleservo_cM_cIT.txt");
    int overwriteFile;
    if (ifile) {
        cout << "WARNING: An angle file for this program already exists. 1: continue and overwrite file, 0: kill program" << endl;
        cin >> overwriteFile;
        if (!overwriteFile)
            exit(0);
    }
    
    anglefile.open("angleservo_cM_cIT.txt", ios::out | ios::trunc); // file to plot angles in function of time
    
    // create (bake?) Loaf object using constructor
    loaf freshLoaf = loaf(numberOfSlices);
    
    float oldslice[13][11] = {{0}}; // stores the last configuration of paddles that was sent to the grid
    float step_size[13][11] = {{0}}; // stores the step size needed to get to the next configuration
    float newslice[13][11] = {{0}}; // stores the step size needed to get to the next configuration
    
    float rms;
    float correction = 1;
    int i = 1; // grid number counter
    int SPACING = 1; // number of interpolations needed to keep servo speed under its max value, in worst case
    
    float amplitude; // for steps/speeds calculations and safety checks, below
    float diff; // difference between two doubles (used with epsilon in place of == operator, which doesn't perform well on doubles)
    float EPSILON = 0.1; // error tolerance for double comparisons (just be within a tenth of a degree)
    int halfLoaf = numberOfSlices / (int) 2; // determine where to set middle slice of loaf (assumes numberOfSlices is odd)
    
    // Normalization calculations (complicated because different correlation functions have different methods for normalization)
    norm = 0; // master norm parameter that's accessible/used from runcorr_3D
    float bound;
    if (typeOfSpatialCorr <= 4 || typeOfSpatialCorr == 10 || typeOfSpatialCorr == 0) bound = range_of_corr;
    else bound = spatial_sigma;
    /* Declare function pointers for the spatial correlation functions
     First, we declare a pointer *pfSpatial/TemporalCorr to a function with 2 arguments j and k (or 1 argument t), plus parameters necessary for function operation (currently sigma and height, though more may need to be added if different kernels or a customizable unsharp are to be implemented).
     Then we call the function pickSpatialCorr (from pickCorrelations.cpp). This function returns a pointer to the function of choice (determined by 'mode'),
     which can then be used by simply calling pfSpatial/TemporalCorr(j, k). */
    
    /* NOTE: We attempted to import sigma, alpha, and height using the pickSpatialCorr/pickTemporalCorr functions, saving them as private global variables in pickCorrelations.cpp.
     This allowed us to simply call pfSpatialCorr(j, k) and pfTemporalCorr(t), removing excess arguments from the runcorr and computermscorr functions, and changing the implementation so that (except in the periodic case) the function pointers would only be created once and then would be passed to the necessary methods as arguments.
     We found this method to be much cleaner, but unfortunately we introduced a bug somewhere along the line. Rather than spend days trying to track it down, we reverted to our previous version and continued taking data. It might be worth re-implementing this feature, if you have time and want to make the code more streamlined. For reference, our previous attempt should still be in a series of commits on GitHub. ~ Nathan Wei and Kevin Griffin
     */
    
    float (*pfSpatialCorr)(int j, int k, float spatial_sigma, float spatial_alpha, float spatial_height);
    float (*pfTemporalCorr)(int t, float temporal_sigma, float temporal_alpha, float temporal_height);
    pfSpatialCorr = pickSpatialCorr(typeOfSpatialCorr);
    pfTemporalCorr = pickTemporalCorr(typeOfTemporalCorr);
    
    // Correlation function work for finding normalization
    // Note: this is different from the previous implementation, which had mysteriously different logic for each function
    for (int j = -bound; j <= bound; j++) { // range of neighbors used to compute normalization/convolution
        for (int k = -bound; k <= bound; k++) { // j and k refer to the shift
            for (int t = -halfLoaf; t <= halfLoaf; t++) {
                norm += (pfSpatialCorr(j, k, spatial_sigma, spatial_alpha, spatial_height) * pfTemporalCorr(t, temporal_sigma, temporal_alpha, temporal_height));
            }
        }
    }
    
    // makes a random correlated sequence of angles, with the same parameters but without correction
    // computes its mean and rms value of angles. This is done so that the rms correction factor can be
    // determined before the angles have been produced
    rms = compute_rmscorr_3D(spatial_sigma, temporal_sigma, typeOfSpatialCorr, typeOfTemporalCorr, spatial_alpha, temporal_alpha, spatial_height, temporal_height, 1, 1, halfLoaf);
    correction = target_rms / rms; // correction factor
    cout << "Done! Correction factor is " << correction << endl << "Setting up timing..." << endl;
    cout << "Done! Starting grid motions" << endl;
    
    //timing:
    // timing uses the standard timeval structure. a timeval struct holds seconds and remaining microseconds. This time is the number of seconds and remaining microseconds since 01.01.1970. Note: once microseconds reaches 10000000, seconds increments and microseconds is set to zero
    timeval startTime; // declare a structure for holding the time that the last slice of angles was sent to the grid
    timeval currentTime; // declare a structure for holding the current time
    long usecElapsed; // a varaible for holding the difference between currentTime and startTime
    gettimeofday(&startTime,0); // initialize startTime with the current time
    
    // ------------ (unclear whether this is actually necessary)
    gettimeofday(&currentTime,0);
    usecElapsed = (currentTime.tv_sec - startTime.tv_sec)*1000000 + ((signed long)currentTime.tv_usec - (signed long)startTime.tv_usec);
    while (usecElapsed <= updatetimeinmus){
        gettimeofday(&currentTime,0);
        usecElapsed = (currentTime.tv_sec - startTime.tv_sec)*1000000 + ((signed long)currentTime.tv_usec - (signed long)startTime.tv_usec);
    }
    while (usecElapsed > updatetimeinmus){
        gettimeofday(&currentTime,0);
        usecElapsed = (signed long)currentTime.tv_usec - (signed long)startTime.tv_usec;
    }
    // ------------
    
    bool firstTime = true; // prevent timing error message from showing up during first iteration
    // main loop: give angle orders (runs until ctrl-C is issued by user, which is caught by signal handler in menuII)
    while(0==0){
        
        //freshLoaf.Loaf_printFullArray(); // debugging
        cout << "\nGrid #" << i << " "; // print grid number
        i += 1;
        // get new slice of angles (using either inlined algorithm or standard function pointer algorithm)
        if (ltfast_on)
            ltfast(newslice, &freshLoaf, halfLoaf, spatial_sigma, temporal_sigma, spatial_alpha, temporal_alpha,
                   spatial_height, temporal_height, correction);
        else if (unsharp_on)
            unsharp(newslice, &freshLoaf, halfLoaf, spatial_sigma, temporal_sigma, spatial_alpha, temporal_alpha,
                    spatial_height, temporal_height, correction);
        else
            runcorr_3D(newslice, &freshLoaf, halfLoaf, spatial_sigma, temporal_sigma, spatial_alpha, temporal_alpha,
                       spatial_height, temporal_height, typeOfSpatialCorr, typeOfTemporalCorr, 0, 0, correction);
        
        // store necessary servo speeds after carrying out safety checks
        for (int row = 0; row < 11; row++) {
            for (int col = 0; col < 13; col++){
                numberOfAnglesSet++; // total number of paddles moved, since the beginning of time (global variable)
                // angle safety processing: do not exceed angle of 90 degrees
                if (newslice[col][row]>90){
                    newslice[col][row]=90;
                    over90orminus90count++; // keep track of number of angles that need to be kept from exceeding +/- 90 degrees
                    //cout << "+"; // print visual representation within histogram if you want
                }
                else if (newslice[col][row]<-90){
                    newslice[col][row]=-90;
                    over90orminus90count++;
                    //cout << "-";
                }
                
                amplitude = newslice[col][row] - oldslice[col][row]; // calculate the amplitude between the old and the new angles
                if (fabs(amplitude)/(max_speed) > SPACING) { // constrain paddles that have to move too far for the servos to handle
                    cout << "*"; // constraining histogram - 1 * = 1 paddle constrained by max speed
                    outOfBoundsCount++; // keep track of number of constrained paddles
                    if (amplitude > 0) step_size[col][row] = max_speed;
                    else if (amplitude < 0) step_size[col][row] = -max_speed;
                }
                // move at constant speed (equal-sized steps over SPACING interpolations) to the target angle
                // this implemenation assumes that servos don't have a minimum speed or angle
                else step_size[col][row] = amplitude/(SPACING);
            }
        }
        
        /* Create SPACING timeslices to separate old and new configurations, and feed each one to the grid in succession.
         * This decouples the grid updating timescale from the correlation computation timescale, and also means we call the
         *   computationally-expensive runcorr_3D method once every SPACING grid configurations.
         * SPACING is currently set to 1, in order to get the highest frequency of correlations possible. With faster computers or a more efficient
         *   runcorr_3D algorithm, increasing SPACING to improve the resolution of the grid motions (while still running 10 correlations per second)
         *   might be possible. */
        for (int t = 0; t < SPACING; t++) {
            //cout << " " << (t+1); // print interpolation number
            
            // compute new intermediate grid position, with steps necessary to attain it
            // adding the calculated servo speed every 0.1 seconds SPACING times guarantees the angle will reach its target by the end of the iteration
            for (int row = 0; row < 11; row++) {
                for(int col = 0; col < 13; col++){
                    if (step_size[col][row] != 0) { // don't bother checking servos that have already arrived
                        diff = fabs(newslice[col][row] - oldslice[col][row]); // determination of approximate equality for doubles
                        if (diff > EPSILON) oldslice[col][row] += step_size[col][row]; // not equal -> add another step
                        else step_size[col][row] = 0; // paddle has arrived; tell servo not to move any more
                    }
                }
            }
            
            //set position of each servo (within 0.1 seconds)
            gettimeofday(&currentTime,0); // set currentTime to hold the current time
            usecElapsed = (currentTime.tv_sec - startTime.tv_sec)*1000000 + ((signed long)currentTime.tv_usec - (signed long)startTime.tv_usec);// useconds elapsed since startTime
            
            if (usecElapsed > updatetimeinmus && !firstTime){ // no need to wait because runcorr took more than .1 sec
                cout << "Time Elapsed is greater than .1 sec.  Time Elapsed = " << usecElapsed;
            }
            else if (usecElapsed < 0){
                assert(0); // assert because something bizarre happened (maybe the timer overflowed somehow) - safety for a case that shouldn't ever occur
            }
            else {
                cout << " " << usecElapsed << "\u03BCs"; // print computation time in microseconds (at end of histogram)
                while (usecElapsed < updatetimeinmus){ // we need to wait
                    gettimeofday(&currentTime,0);
                    usecElapsed = (currentTime.tv_sec - startTime.tv_sec)*1000000 + ((signed long)currentTime.tv_usec - (signed long)startTime.tv_usec);
                }
            }
            // record new start time to compare against for next iteration
            gettimeofday(&startTime,0);
            
            if (firstTime) firstTime = false; // set to false for remainder of run
            
            //----------------
            setanglestoallservosIII(oldslice, step_size, constantArea, target_rms); // for motion - send generated 2D arrays to grid communication method
        }
    }
    anglefile.close(); // never reaches this point
    return 0; // never reaches this point
}
Ejemplo n.º 8
0
Font*
openfont1(Display *d, char *name)
{
	Font *fnt;
	int fd, i, n, scale;
	char *buf, *nambuf, *fname, *freename;

	nambuf = 0;
	freename = nil;
	scale = parsefontscale(name, &fname);

	fd = open(fname, OREAD);
	if(fd < 0 && strncmp(fname, "/lib/font/bit/", 14) == 0){
		nambuf = smprint("#9/font/%s", fname+14);
		if(nambuf == nil)
			return 0;
		nambuf = unsharp(nambuf);
		if(nambuf == nil)
			return 0;
		if((fd = open(nambuf, OREAD)) < 0){
			free(nambuf);
			return 0;
		}
		fname = nambuf;
		if(scale > 1) {
			name = smprint("%d*%s", scale, fname);
			freename = name;
		} else {
			name = fname;
		}
	}
	if(fd >= 0)
		n = _drawflength(fd);
	if(fd < 0 && strncmp(fname, "/mnt/font/", 10) == 0) {
		fd = _fontpipe(fname+10);
		n = 8192;
	}
	if(fd < 0)
		return 0;

	buf = malloc(n+1);
	if(buf == 0){
		close(fd);
		free(nambuf);
		return 0;
	}
	i = readn(fd, buf, n);
	close(fd);
	if(i <= 0){
		free(buf);
		free(nambuf);
		return 0;
	}
	buf[i] = 0;
	fnt = buildfont(d, buf, name);
	free(buf);
	free(nambuf);
	free(freename);
	if(scale != 1) {
		fnt->scale = scale;
		fnt->height *= scale;
		fnt->ascent *= scale;
		fnt->width *= scale;
	}
	return fnt;
}
Ejemplo n.º 9
0
void
main(int argc, char **argv)
{
	int i, hdr, n, eof, off;
	Dreprog *re[3];
	int m[3];
	char *p, *ep, *tag;
	Biobuf bout, bin;
	char msg[1024+1];
	char buf[1024];

	refile = unsharp(refile);
	buildre(re);
	ARGBEGIN{
	case 'D':
		debug = 1;
		break;
	case 'n':
		maxtoklen = atoi(EARGF(usage()));
		break;
	case 'r':
		refile = EARGF(usage());
		break;
	default:
		usage();
	}ARGEND;

	if(argc > 1)
		usage();
	if(argc == 1){
		close(0);
		if(open(argv[0], OREAD) < 0)
			sysfatal("open %s: %r", argv[0]);
	}

	tag = nil;
	Binit(&bin, 0, OREAD);
	Binit(&bout, 1, OWRITE);
	ep = msg;
	p = msg;
	eof = 0;
	off = 0;
	hdr = 1;
	for(;;){
		/* replenish buffer */
		if(ep - p < 512 && !eof){
			if(p > msg + 1){
				n = ep - p;
				memmove(msg, p-1, ep-(p-1));
				off += (p-1) - msg;
				p = msg+1;
				ep = p + n;
			}
			n = Bread(&bin, ep, msg+(sizeof msg - 1)- ep);
			if(n < 0)
				sysfatal("read error: %r");
			if(n == 0)
				eof = 1;
			ep += n;
			*ep = 0;
		}
		if(p >= ep)
			break;

		if(*p == 0){
			p++;
			continue;
		}

		if(hdr && p[-1]=='\n'){
			if(p[0]=='\n')
				hdr = 0;
			else if(cistrncmp(p-1, "\nfrom:", 6) == 0)
				tag = "From*";
			else if(cistrncmp(p-1, "\nto:", 4) == 0)
				tag = "To*";
			else if(cistrncmp(p-1, "\nsubject:", 9) == 0)
				tag = "Subject*";
			else if(cistrncmp(p-1, "\nreturn-path:", 13) == 0)
				tag = "Return-Path*";
			else
				tag = nil;
		}
		m[0] = dregexec(re[0], p, p==msg || p[-1]=='\n');
		m[1] = dregexec(re[1], p, p==msg || p[-1]=='\n');
		m[2] = dregexec(re[2], p, p==msg || p[-1]=='\n');

		n = m[0];
		if(n < m[1])
			n = m[1];
		if(n < m[2])
			n = m[2];
		if(n <= 0){
fprint(2, "«%s» %.2ux", p, p[0]);
			sysfatal("no regexps matched at %ld", off + (p-msg));
		}

		if(m[0] >= m[1] && m[0] >= m[2]){
			/* "From " marks start of new message */
			Bprint(&bout, "*From*\n");
			n = m[0];
			hdr = 1;
		}else if(m[2] > 1){
			/* ignore */
			n = m[2];
		}else if(m[1] >= m[0] && m[1] >= m[2] && m[1] > 2 && m[1] <= maxtoklen){
			/* keyword */
			/* should do UTF-aware lowercasing, too much bother */
/*
			for(i=0; i<n; i++)
				if('A' <= p[i] && p[i] <= 'Z')
					p[i] += 'a' - 'A';
*/
			if(tag){
				i = strlen(tag);	
				memmove(buf, tag, i);
				memmove(buf+i, p, m[1]);
				buf[i+m[1]] = 0;
			}else{
				memmove(buf, p, m[1]);
				buf[m[1]] = 0;
			}
			Bprint(&bout, "%s\n", buf);
			while(trim(buf) >= 0)
				Bprint(&bout, "stem*%s\n", buf);
			n = m[1];
		}else
			n = m[2];
		if(debug)
			fprint(2, "%.*s¦", utfnlen(p, n), p);
		p += n;
	}
	Bterm(&bout);
	exits(0);
}
Ejemplo n.º 10
0
void n_ptinit(void)
{
	int i;
	char *p;
	char opt[50], cmd[100];
	FILE *fp;

	hmot = n_hmot;
	makem = n_makem;
	setabs = n_setabs;
	setch = n_setch;
	sethl = n_sethl;
	setht = n_setht;
	setslant = n_setslant;
	vmot = n_vmot;
	xlss = n_xlss;
	findft = n_findft;
	width = n_width;
	mchbits = n_mchbits;
	ptlead = n_ptlead;
	ptout = n_ptout;
	ptpause = n_ptpause;
	setfont = n_setfont;
	setps = n_setps;
	setwd = n_setwd;

	if ((p = getenv("NROFFTERM")) != 0)
		strcpy(devname, p);
	if (termtab[0] == 0)
		strcpy(termtab,DWBntermdir);
	if (fontdir[0] == 0)
		strcpy(fontdir, "");
	if (devname[0] == 0)
		strcpy(devname, NDEVNAME);
	pl = 11*INCH;
	po = PO;
	hyf = 0;
	ascii = 1;
	lg = 0;
	fontlab[1] = 'R';
	fontlab[2] = 'I';
	fontlab[3] = 'B';
	fontlab[4] = PAIR('B','I');
	fontlab[5] = 'D';
	bdtab[3] = 3;
	bdtab[4] = 3;

	/* hyphalg = 0;	/* for testing */

	strcat(termtab, devname);
	if ((fp = fopen(unsharp(termtab), "r")) == NULL) {
		ERROR "cannot open %s", termtab WARN;
		exit(-1);
	}


/* this loop isn't robust about input format errors. */
/* it assumes  name, name-value pairs..., charset */
/* god help us if we get out of sync. */

	fscanf(fp, "%s", cmd);	/* should be device name... */
	if (!is(devname) && trace)
		ERROR "wrong terminal name: saw %s, wanted %s", cmd, devname WARN;
	for (;;) {
		fscanf(fp, "%s", cmd);
		if (is("charset"))
			break;
		fscanf(fp, " %[^\n]", opt);
		if (is("bset")) t.bset = atoi(opt);
		else if (is("breset")) t.breset = atoi(opt);
		else if (is("Hor")) t.Hor = atoi(opt);
		else if (is("Vert")) t.Vert = atoi(opt);
		else if (is("Newline")) t.Newline = atoi(opt);
		else if (is("Char")) t.Char = atoi(opt);
		else if (is("Em")) t.Em = atoi(opt);
		else if (is("Halfline")) t.Halfline = atoi(opt);
		else if (is("Adj")) t.Adj = atoi(opt);
		else if (is("twinit")) t.twinit = strdupl(parse(opt, Notype));
		else if (is("twrest")) t.twrest = strdupl(parse(opt, Notype));
		else if (is("twnl")) t.twnl = strdupl(parse(opt, Notype));
		else if (is("hlr")) t.hlr = strdupl(parse(opt, Notype));
		else if (is("hlf")) t.hlf = strdupl(parse(opt, Notype));
		else if (is("flr")) t.flr = strdupl(parse(opt, Notype));
		else if (is("bdon")) t.bdon = strdupl(parse(opt, Notype));
		else if (is("bdoff")) t.bdoff = strdupl(parse(opt, Notype));
		else if (is("iton")) t.iton = strdupl(parse(opt, Notype));
		else if (is("itoff")) t.itoff = strdupl(parse(opt, Notype));
		else if (is("ploton")) t.ploton = strdupl(parse(opt, Notype));
		else if (is("plotoff")) t.plotoff = strdupl(parse(opt, Notype));
		else if (is("up")) t.up = strdupl(parse(opt, Notype));
		else if (is("down")) t.down = strdupl(parse(opt, Notype));
		else if (is("right")) t.right = strdupl(parse(opt, Notype));
		else if (is("left")) t.left = strdupl(parse(opt, Notype));
		else
			ERROR "bad tab.%s file, %s %s", devname, cmd, opt WARN;
	}

	getnrfont(fp);
	fclose(fp);

	sps = EM;
	ics = EM * 2;
	dtab = 8 * t.Em;
	for (i = 0; i < 16; i++)
		tabtab[i] = dtab * (i + 1);
	pl = 11 * INCH;
	po = PO;
	spacesz = SS;
	lss = lss1 = VS;
	ll = ll1 = lt = lt1 = LL;
	smnt = nfonts = 5;	/* R I B BI S */
	n_specnames();	/* install names like "hyphen", etc. */
	if (eqflg)
		t.Adj = t.Hor;
}
Ejemplo n.º 11
0
void
mesgsend(Message *m)
{
	char *s, *body, *to;
	int i, j, h, n, natt, p[2];
	struct Exec *e;
	Channel *sync;
	int first, nfld, delit, ofd;
	char *copy, *fld[100], *now;

	body = winreadbody(m->w, &n);
	/* assemble to: list from first line, to: line, and cc: line */
	nto = 0;
	natt = 0;
	ncc = 0;
	nbcc = 0;
	first = 1;
	to = body;
	for(;;){
		for(s=to; *s!='\n'; s++)
			if(*s == '\0'){
				free(body);
				return;
			}
		if(s++ == to)	/* blank line */
			break;
		/* make copy of line to tokenize */
		copy = emalloc(s-to);
		memmove(copy, to, s-to);
		copy[s-to-1] = '\0';
		nfld = tokenizec(copy, fld, nelem(fld), ", \t");
		if(nfld == 0){
			free(copy);
			break;
		}
		n -= s-to;
		switch(h = whichheader(fld[0])){
		case TO:
		case FROM:
			delit = 1;
			commas(to+strlen(fld[0]), s-1);
			for(i=1; i<nfld && nto<nelem(tolist); i++)
				if(!addressed(fld[i]))
					tolist[nto++] = estrdup(fld[i]);
			break;
		case BCC:
			delit = 1;
			commas(to+strlen(fld[0]), s-1);
			for(i=1; i<nfld && nbcc<nelem(bcclist); i++)
				if(!addressed(fld[i]))
					bcclist[nbcc++] = estrdup(fld[i]);
			break;
		case CC:
			delit = 1;
			commas(to+strlen(fld[0]), s-1);
			for(i=1; i<nfld && ncc<nelem(cclist); i++)
				if(!addressed(fld[i]))
					cclist[ncc++] = estrdup(fld[i]);
			break;
		case ATTACH:
		case INCLUDE:
			delit = 1;
			for(i=1; i<nfld && natt<nelem(attlist); i++){
				attlist[natt] = estrdup(fld[i]);
				included[natt++] = (h == INCLUDE);
			}
			break;
		default:
			if(first){
				delit = 1;
				for(i=0; i<nfld && nto<nelem(tolist); i++)
					tolist[nto++] = estrdup(fld[i]);
			}else	/* ignore it */
				delit = 0;
			break;
		}
		if(delit){
			/* delete line from body */
			memmove(to, s, n+1);
		}else
			to = s;
		free(copy);
		first = 0;
	}

	ofd = open(outgoing, OWRITE|OCEXEC);	/* no error check necessary */
	if(ofd > 0){
		/* From dhog Fri Aug 24 22:13:00 EDT 2001 */
		now = ctime(time(0));
		seek(ofd, 0, 2);
		fprint(ofd, "From %s %s", user, now);
		fprint(ofd, "From: %s\n", user);
		fprint(ofd, "Date: %s", now);
		for(i=0; i<natt; i++)
			if(included[i])
				fprint(ofd, "Include: %s\n", attlist[i]);
			else
				fprint(ofd, "Attach: %s\n", attlist[i]);
		/* needed because mail is by default Latin-1 */
		fprint(ofd, "Content-Type: text/plain; charset=\"UTF-8\"\n");
		fprint(ofd, "Content-Transfer-Encoding: 8bit\n");
	}

	e = emalloc(sizeof(struct Exec));
	if(pipe(p) < 0)
		error("can't create pipe: %r");
	e->p[0] = p[0];
	e->p[1] = p[1];
	e->prog = unsharp("#9/bin/upas/marshal");
	e->argv = emalloc((1+1+2+4*natt+1)*sizeof(char*));
	e->argv[0] = estrdup("marshal");
	e->argv[1] = estrdup("-8");
	j = 2;
	if(m->replyname){
		e->argv[j++] = estrdup("-R");
		e->argv[j++] = estrstrdup(mbox.name, m->replyname);
	}
	for(i=0; i<natt; i++){
		if(included[i])
			e->argv[j++] = estrdup("-A");
		else
			e->argv[j++] = estrdup("-a");
		e->argv[j++] = estrdup(attlist[i]);
	}
	sync = chancreate(sizeof(int), 0);
	e->sync = sync;
	proccreate(execproc, e, EXECSTACK);
	recvul(sync);
	/* close(p[0]); */

	/* using marshal -8, so generate rfc822 headers */
	if(nto > 0){
		print2(p[1], ofd, "To: ");
		for(i=0; i<nto-1; i++)
			print2(p[1], ofd, "%s, ", tolist[i]);
		print2(p[1], ofd, "%s\n", tolist[i]);
	}
	if(ncc > 0){
		print2(p[1], ofd, "CC: ");
		for(i=0; i<ncc-1; i++)
			print2(p[1], ofd, "%s, ", cclist[i]);
		print2(p[1], ofd, "%s\n", cclist[i]);
	}
	if(nbcc > 0){
		print2(p[1], ofd, "BCC: ");
		for(i=0; i<nbcc-1; i++)
			print2(p[1], ofd, "%s, ", bcclist[i]);
		print2(p[1], ofd, "%s\n", bcclist[i]);
	}

	i = strlen(body);
	if(i > 0)
		write2(p[1], ofd, body, i, 1);

	/* guarantee a blank line, to ensure attachments are separated from body */
	if(i==0 || body[i-1]!='\n')
		write2(p[1], ofd, "\n\n", 2, 0);
	else if(i>1 && body[i-2]!='\n')
		write2(p[1], ofd, "\n", 1, 0);

	/* these look like pseudo-attachments in the "outgoing" box */
	if(ofd>0 && natt>0){
		for(i=0; i<natt; i++)
			if(included[i])
				fprint(ofd, "=====> Include: %s\n", attlist[i]);
			else
				fprint(ofd, "=====> Attach: %s\n", attlist[i]);
	}
	if(ofd > 0)
		write(ofd, "\n", 1);

	for(i=0; i<natt; i++)
		free(attlist[i]);
	close(ofd);
	close(p[1]);
	free(body);

	if(m->replyname != nil)
		mesgmenumark(mbox.w, m->replyname, "\t[replied]");
	if(m->name[0] == '/')
		s = estrdup(m->name);
	else
		s = estrstrdup(mbox.name, m->name);
	s = egrow(s, "-R", nil);
	winname(m->w, s);
	free(s);
	winclean(m->w);
	/* mark message unopened because it's no longer the original message */
	m->opened = 0;
}
Ejemplo n.º 12
0
void
threadmain(int argc, char **argv)
{
	int i;
	int nofork;
	char *prog;
	Job *j;
	
	ventilogging = 1;
	ventifmtinstall();
#ifdef PLAN9PORT
	bin = unsharp("#9/bin/venti");
#else
	bin = "/bin/venti";
#endif
	nofork = 0;
	ARGBEGIN{
	case 'b':
		bin = EARGF(usage());
		break;
	case 's':
		nofork = 1;
		break;
	default:
		usage();
	}ARGEND

	if(argc != 1)
		usage();
	if(rdconf(argv[0], &conf) < 0)
		sysfatal("reading config: %r");
	if(conf.httpaddr == nil)
		sysfatal("config has no httpaddr");
	if(conf.smtp != nil && conf.mailfrom == nil)
		sysfatal("config has smtp but no mailfrom");
	if(conf.smtp != nil && conf.mailto == nil)
		sysfatal("config has smtp but no mailto");
	if((mirrorprog = regcomp(mirrorregexp)) == nil)
		sysfatal("mirrorregexp did not complete");
	if((verifyprog = regcomp(verifyregexp)) == nil)
		sysfatal("verifyregexp did not complete");
	if(conf.nverify > 0 && conf.verifyfreq == 0)
		sysfatal("config has no verifyfreq");
	if(conf.nmirror > 0 && conf.mirrorfreq == 0)
		sysfatal("config has no mirrorfreq");

	time0 = time(0);
//	sendmail("startup", "mgr is starting\n");

	logbuf = vtmalloc(LogSize+1);	// +1 for NUL

	errlog = vtlogopen("errors", LogSize);
	job = vtmalloc((conf.nmirror+conf.nverify)*sizeof job[0]);
	prog = smprint("%s/mirrorarenas", bin);
	for(i=0; i<conf.nmirror; i++) {
		// job: /bin/venti/mirrorarenas -v src dst
		// filter output
		j = &job[njob++];
		mkjob(j, prog, "-v", conf.mirror[i].src, conf.mirror[i].dst, nil);
		j->name = smprint("mirror %s %s", conf.mirror[i].src, conf.mirror[i].dst);
		j->ok = mirrorok;
		j->freq = conf.mirrorfreq;	// 4 hours	// TODO: put in config
		j->offset = (double)i/conf.nmirror;
	}

	prog = smprint("%s/verifyarena", bin);
	for(i=0; i<conf.nverify; i++) {
		// job: /bin/venti/verifyarena -b 64M -s 1000 -v arena
		// filter output
		j = &job[njob++];
		mkjob(j, prog, "-b64M", "-s1000", conf.verify[i], nil);
		j->name = smprint("verify %s", conf.verify[i]);
		j->ok = verifyok;
		j->freq = conf.verifyfreq;
		j->offset = (double)i/conf.nverify;
	}

	httpdobj("/mgr", hmanager);
	httpdobj("/log", xlog);
	vtproc(httpdproc, conf.httpaddr);
	vtproc(waitproc, threadwaitchan());
	if(nofork)
		manager(nil);
	else
		vtproc(manager, nil);
}
Ejemplo n.º 13
0
void
rwrite(Job *job, Mfile *mf, Request *req)
{
	int cnt, rooted, status;
	long n;
	char *err, *p, *atype;
	RR *rp, *tp, *neg;
	int wantsav;
	static char *dumpfile;

	err = 0;
	cnt = job->request.count;
	if(mf->qid.type & QTDIR){
		err = "can't write directory";
		goto send;
	}
	if(cnt >= Maxrequest){
		err = "request too long";
		goto send;
	}
	job->request.data[cnt] = 0;
	if(cnt > 0 && job->request.data[cnt-1] == '\n')
		job->request.data[cnt-1] = 0;

	/*
	 *  special commands
	 */
	p = job->request.data;
	if(strcmp(p, "debug")==0){
		debug ^= 1;
		goto send;
	} else if(strcmp(p, "dump")==0){
		if(dumpfile == nil)
			dumpfile = unsharp("#9/ndb/dnsdump");
		dndump(dumpfile);
		goto send;
	} else if(strncmp(p, "dump ", 5) == 0){
		if(*(p+5))
			dndump(p+5);
		else
			err = "bad filename";
		goto send;
	} else if(strcmp(p, "refresh")==0){
		needrefresh = 1;
		goto send;
	}

	/*
	 *  kill previous reply
	 */
	mf->nrr = 0;
	mf->rr[0] = 0;

	/*
	 *  break up request (into a name and a type)
	 */
	atype = strchr(job->request.data, ' ');
	if(atype == 0){
		err = "illegal request";
		goto send;
	} else
		*atype++ = 0;

	/*
	 *  tracing request
	 */
	if(strcmp(atype, "trace") == 0){
		if(trace)
			free(trace);
		if(*job->request.data)
			trace = estrdup(job->request.data);
		else
			trace = 0;
		goto send;
	}

	mf->type = rrtype(atype);
	if(mf->type < 0){
		err = "unknown type";
		goto send;
	}

	p = atype - 2;
	if(p >= job->request.data && *p == '.'){
		rooted = 1;
		*p = 0;
	} else
		rooted = 0;

	p = job->request.data;
	if(*p == '!'){
		wantsav = 1;
		p++;
	} else
		wantsav = 0;
	dncheck(0, 1);
	rp = dnresolve(p, Cin, mf->type, req, 0, 0, Recurse, rooted, &status);
	dncheck(0, 1);
	neg = rrremneg(&rp);
	if(neg){
		status = neg->negrcode;
		rrfreelist(neg);
	}
	if(rp == 0){
		switch(status){
		case Rname:
			err = "name does not exist";
			break;
		case Rserver:
			err = "dns failure";
			break;
		default:
			err = "resource does not exist";
			break;
		}
	} else {
		lock(&joblock);
		if(!job->flushed){
			/* format data to be read later */
			n = 0;
			mf->nrr = 0;
			for(tp = rp; mf->nrr < Maxrrr-1 && n < Maxreply && tp &&
					tsame(mf->type, tp->type); tp = tp->next){
				mf->rr[mf->nrr++] = n;
				if(wantsav)
					n += snprint(mf->reply+n, Maxreply-n, "%Q", tp);
				else
					n += snprint(mf->reply+n, Maxreply-n, "%R", tp);
			}
			mf->rr[mf->nrr] = n;
		}
		unlock(&joblock);
		rrfreelist(rp);
	}

    send:
	dncheck(0, 1);
	job->reply.count = cnt;
	sendmsg(job, err);
}
Ejemplo n.º 14
0
int
main(int argc, char **argv)
{
	int isnew;
	char *id, buf[Maxmsg], home[Maxmsg], prompt[100], *hexHi;
	char *pass, *passck;
	long expsecs;
	mpint *H = mpnew(0), *Hi = mpnew(0);
	PW *pw;
	Tm *tm;

	SECSTORE_DIR = unsharp("#9/secstore");

	ARGBEGIN{
	case 'v':
		verbose++;
		break;
	}ARGEND;
	if(argc!=1){
		print("usage: secuser [-v] <user>\n");
		exits("usage");
	}

	ensure_exists(SECSTORE_DIR, DMDIR|0755L);
	snprint(home, sizeof(home), "%s/who", SECSTORE_DIR);
	ensure_exists(home, DMDIR|0755L);
	snprint(home, sizeof(home), "%s/store", SECSTORE_DIR);
	ensure_exists(home, DMDIR|0700L);

	id = argv[0];
	if(verbose)
		fprint(2,"secuser %s\n", id);
	if((pw = getPW(id,1)) == nil){
		isnew = 1;
		print("new account (because %s/%s %r)\n", SECSTORE_DIR, id);
		pw = emalloc(sizeof(*pw));
		pw->id = estrdup(id);
		snprint(home, sizeof(home), "%s/store/%s", SECSTORE_DIR, id);
		if(access(home, AEXIST) == 0){
			print("new user, but directory %s already exists\n", home);
			exits(home);
		}
	}else{
		isnew = 0;
	}

	/* get main password for id */
	for(;;){
		if(isnew)
			snprint(prompt, sizeof(prompt), "%s password", id);
		else
			snprint(prompt, sizeof(prompt), "%s password [default = don't change]", id);
		pass = readcons(prompt, nil, 1);
		if(pass == nil){
			print("getpass failed\n");
			exits("getpass failed");
		}
		if(verbose)
			print("%ld characters\n", strlen(pass));
		if(pass[0] == '\0' && isnew == 0)
			break;
		if(strlen(pass) >= 7)
			break;
		print("password must be at least 7 characters\n");
	}

	if(pass[0] != '\0'){
		snprint(prompt, sizeof(prompt), "retype password");
		if(verbose)
			print("confirming...\n");
		passck = readcons(prompt, nil, 1);
		if(passck == nil){
			print("getpass failed\n");
			exits("getpass failed");
		}
		if(strcmp(pass, passck) != 0){
			print("passwords didn't match\n");
			exits("no match");
		}
		memset(passck, 0, strlen(passck));
		free(passck);
		hexHi = PAK_Hi(id, pass, H, Hi);
		memset(pass, 0, strlen(pass));
		free(pass);
		free(hexHi);
		mpfree(H);
		pw->Hi = Hi;
	}

	/* get expiration time (midnight of date specified) */
	if(isnew)
		expsecs = time(0) + 365*24*60*60;
	else
		expsecs = pw->expire;

	for(;;){
		tm = localtime(expsecs);
		print("expires [DDMMYYYY, default = %2.2d%2.2d%4.4d]: ",
				tm->mday, tm->mon, tm->year+1900);
		userinput(buf, sizeof(buf));
		if(strlen(buf) == 0)
			break;
		if(strlen(buf) != 8){
			print("!bad date format: %s\n", buf);
			continue;
		}
		tm->mday = (buf[0]-'0')*10 + (buf[1]-'0');
		if(tm->mday > 31 || tm->mday < 1){
			print("!bad day of month: %d\n", tm->mday);
			continue;
		}
		tm->mon = (buf[2]-'0')*10 + (buf[3]-'0') - 1;
		if(tm->mon > 11 || tm->mday < 0){
			print("!bad month: %d\n", tm->mon + 1);
			continue;
		}
		tm->year = atoi(buf+4) - 1900;
		if(tm->year < 70){
			print("!bad year: %d\n", tm->year + 1900);
			continue;
		}
		tm->sec = 59;
		tm->min = 59;
		tm->hour = 23;
		tm->yday = 0;
		expsecs = tm2sec(tm);
		break;
	}
	pw->expire = expsecs;

	/* failed logins */
	if(pw->failed != 0 )
		print("clearing %d failed login attempts\n", pw->failed);
	pw->failed = 0;

	/* status bits */
	if(isnew)
		pw->status = Enabled;
	for(;;){
		print("Enabled or Disabled [default %s]: ",
			(pw->status & Enabled) ? "Enabled" : "Disabled" );
		userinput(buf, sizeof(buf));
		if(strlen(buf) == 0)
			break;
		if(buf[0]=='E' || buf[0]=='e'){
			pw->status |= Enabled;
			break;
		}
		if(buf[0]=='D' || buf[0]=='d'){
			pw->status = pw->status & ~Enabled;
			break;
		}
	}
	for(;;){
		print("require STA? [default %s]: ",
			(pw->status & STA) ? "yes" : "no" );
		userinput(buf, sizeof(buf));
		if(strlen(buf) == 0)
			break;
		if(buf[0]=='Y' || buf[0]=='y'){
			pw->status |= STA;
			break;
		}
		if(buf[0]=='N' || buf[0]=='n'){
			pw->status = pw->status & ~STA;
			break;
		}
	}

	/* free form field */
	if(isnew)
		pw->other = nil;
	print("comments [default = %s]: ", (pw->other == nil) ? "" : pw->other);
	userinput(buf, 72);  /* 72 comes from password.h */
	if(buf[0])
		if((pw->other = strdup(buf)) == nil)
			sysfatal("strdup");

	syslog(0, LOG, "CHANGELOGIN for '%s'", pw->id);
	if(putPW(pw) < 0){
		print("error writing entry: %r\n");
		exits("can't write password file");
	}else{
		print("change written\n");
		if(isnew && create(home, OREAD, DMDIR | 0775L) < 0){
			print("unable to create %s: %r\n", home);
			exits(home);
		}
	}

	exits("");
	return 1;  /* keep  other compilers happy */
}