The comprehensive practice of Unix system programming open, read, write and lseek

Keywords: Programming Makefile Linker

Requirement: the first command line parameter of the program is the name of the file to be opened, and the remaining parameters specify the input and output operations to be performed on the file. Each parameter representing an operation begins with a letter, followed by a related value (separated by no spaces).

soffet: retrieve offset byte position from file

rlength: read the length byte data from the file at the current offset of the file, and explicitly

Rlength: reads the length byte data from the file at the offset of the current file, and explicitly displays it in hex

wstr: at the current file offset, the string specified by str is written by the file

main.c

#include <sys/stat.h>
#include <fcntl.h>
#include <ctype.h>
#include "get_num.h"
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main(int argc, char *argv[]) {
        size_t  len;
        off_t offset;
        int fd, ap, j;
        char *buf;
        ssize_t numRead, numWritten;

  /* usage */
        if(argc < 3 || strcmp(argv[1], "--help") == 0)
                printf("%s file {r<length> | R<length> | w<string> | s<offset>} ...\n",
                                                 argv[0]);

        /* open or create file */
        fd = open(argv[1], O_RDWR | O_CREAT, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP |
                                                S_IROTH | S_IWOTH);

        /* is system call success */
        if(fd == -1)
                printf("open file error\n");

        /* biz code */
        for(ap = 2; ap < argc; ap++) {
                switch(argv[ap][0]) {
                case 'r':                   /* Display bytes at current offset, as text */
                case 'R':                   /* Display bytes at current offset, in hex */
                        len = getLong(&argv[ap][1], GN_ANY_BASE, argv[ap]);

                /* alloc buffer */
                buf = malloc(len);

                /* is alloc success */
                if(buf == NULL)
                        printf("malloc error\n");

                numRead = read(fd, buf, len);
                if(numRead == -1)
                        /* read fail */
                        printf("read\n");

                /* end of file */
                if(numRead == 0)
                        printf("%s: end-of-file\n", argv[ap]);
                else {
                        printf("%s: ", argv[ap]);
                        for(j=0; j<numRead; j++)
                                if(argv[ap][0] == 'r')
                                        printf("%c", isprint((unsigned char) buf[j]) ? buf[j] : '?');
                                else
                                        printf("%O2x ", (unsigned int) buf[j]);
                        printf("\n");
                }

                /* free memory */
                free(buf);
                break;
                case 'w':                   /* Write string at current offset */
                        numWritten = write(fd, &argv[ap][1], strlen(&argv[ap][1]));
                        if(numWritten == -1)
                                printf("write error\n");
                        printf("%s: wrote %ld bytes\n", argv[ap], (long) numWritten);
                        break;
                case 's':
                        offset = getLong(&argv[ap][1], GN_ANY_BASE, argv[ap]);
                        if(lseek(fd, offset, SEEK_SET) == -1)
                                printf("lseek error!\n");
                        printf("%s: seek successed\n", argv[ap]);
                        break;
                default:
                        printf("Argument must start with [rRws]: %s\n", argv[ap]);
                }
        }

        exit(0);
}

get_num.h

#ifndef GET_NUM_H
#define GET_NUM_H

#define GN_NONNEG          01   /* Value must be >= 0 */
#define GN_GT_0            02   /* Value must be > 0 */

                                /* By default, integers are decimal */
#define GN_ANY_BASE      0100   /* Can use any base - like strtol(3) */
#define GN_BASE_8        0200   /* Value is expressed in octal */
#define GN_BASE_16       0400   /* Value is expressed in hexadecimal */

long getLong(const char *arg, int flags, const char *name);

int getInt(const char *arg, int flags, const char *name);

#endif

get_num.c

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <limits.h>
#include <errno.h>
#include "get_num.h"

static void gnFail(const char *fname, const char *msg, const char *arg,
                   const char *name) {
  fprintf(stderr, "%s error", fname);

  if (name != NULL)
    fprintf(stderr, " (in %s)", name);

  fprintf(stderr, ": %s\n", name);

  if(arg != NULL && *arg != '\0')
    fprintf(stderr, "        offending text: %s\n", arg);

  exit(EXIT_FAILURE);
}

static long getNum(const char *fname, const char *arg, int flags,
                   const char *name)
{
  long res;
  char *endptr;
  int base;

  if(arg == NULL || *arg == '\0')
    gnFail(fname, "null or empty string", arg, name);

  base = (flags & GN_ANY_BASE) ? 0 : (flags & GN_BASE_8) ? 8 :
    (flags & GN_BASE_16) ? 16 : 10;

  errno = 0;

  res = strtol(arg, &endptr, base);

  if(errno != 0)
    gnFail(fname, "strtol() failed", arg, name);

  if(*endptr != '\0')
    gnFail(fname, "nonnumeric characters", arg, name);

  if((flags & GN_NONNEG) && res < 0)
    gnFail(fname, "negative value not allowed", arg, name);

  if((flags & GN_GT_0) && res <= 0)
    gnFail(fname, "value must be > 0", arg, name);

  return res;
}

long getLong(const char *arg, int flags, const char *name)
{
  return getNum("getLong", arg, flags, name);
}

int getInt(const char *arg, int flags, const char *name)
{
  long res;

  res = getNum("getInt", arg, flags, name);
  if(res > INT_MAX || res < INT_MIN)
    gnFail("getInt", "integer out of range", arg, name);

  return (int) res;
}

Write a Makefile

# Edit the following for your installation

CC      =       /usr/bin/cc
CXX     =       /usr/bin/g++

#==============================================================
# Compiler and linker flags
CXXFLAGS=       -Wall -g
CFLAGS  =       -Wall -g
LFLAGS  =
INCPATH =
LIBS=
####### Implicit rules

.SUFFIXES: .o .c .cpp .cc .cxx .C

.cpp.o:
        $(CXX) -c $(CXXFLAGS) $(INCPATH) -o "$@" "$<"

.cc.o:
        $(CXX) -c $(CXXFLAGS) $(INCPATH) -o "$@" "$<"

.cxx.o:
        $(CXX) -c $(CXXFLAGS) $(INCPATH) -o "$@" "$<"

.C.o:
        $(CXX) -c $(CXXFLAGS) $(INCPATH) -o "$@" "$<"

.c.o:
        $(CC) -c $(CFLAGS) $(INCPATH) -o "$@" "$<"



#==============================================================
# This program's code files
prg             =       seektest
OBJS    =       main.o  get_num.o

#==============================================================
# File dependencies and rules

all:    $(prg)

$(prg): $(OBJS)
        $(CC) -o $@ $(LFLAGS) $(OBJS) $(LIBS)

clean:
        rm -f *~
        rm -f *.o
        rm -f core

cleanall:clean
        rm -f $(prg)

Posted by chiaki*misu on Thu, 31 Oct 2019 16:03:07 -0700